0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-15 16:31:25 -05:00

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

This commit is contained in:
Andrey Antukh 2024-06-11 11:27:16 +02:00
commit c0919aff51
110 changed files with 1849 additions and 1757 deletions
CHANGES.md
common
src/app/common
test/common_tests/logic
frontend

View file

@ -20,15 +20,17 @@
### :sparkles: New features
- Fix clickable area of Penptot logo in the viewer [Taiga #7988](https://tree.taiga.io/project/penpot/issue/7988)
- Fix constraints dropdown when selecting multiple shapes [Taiga #7686](https://tree.taiga.io/project/penpot/issue/7686)
- Improve auth process [Taiga #7094](https://tree.taiga.io/project/penpot/us/7094)
- Add locking degrees increment (hold shift) on path edition [Taiga #7761](https://tree.taiga.io/project/penpot/issue/7761)
- Persistence & Concurrent Edition Enhancements [Taiga #5657](https://tree.taiga.io/project/penpot/us/5657)
- Allow library colors as recent colors [Taiga #7640](https://tree.taiga.io/project/penpot/issue/7640)
- Missing scroll in viewmode comments [Taiga #7427](https://tree.taiga.io/project/penpot/issue/7427)
- Comments in View mode should mimic the positioning behavior of the Workspace [Taiga #7346](https://tree.taiga.io/project/penpot/issue/7346)
### :bug: Bugs fixed
- Fix clickable area of Penptot logo in the viewer [Taiga #7988](https://tree.taiga.io/project/penpot/issue/7988)
- Fix constraints dropdown when selecting multiple shapes [Taiga #7686](https://tree.taiga.io/project/penpot/issue/7686)
- Layout and scrollign fixes for the bottom palette [Taiga #7559](https://tree.taiga.io/project/penpot/issue/7559)
- Fix expand libraries when search results are present [Taiga #7876](https://tree.taiga.io/project/penpot/issue/7876)
- Fix color palette default library [Taiga #8029](https://tree.taiga.io/project/penpot/issue/8029)
@ -39,6 +41,9 @@
- Fix "Share prototypes" modal remains open [Taiga #7442](https://tree.taiga.io/project/penpot/issue/7442)
- Fix "Components visibility and opacity" [#4694](https://github.com/penpot/penpot/issues/4694)
- Fix "Attribute overrides in copies are not exported in zip file" [Taiga #8072](https://tree.taiga.io/project/penpot/issue/8072)
- Fix group not automatically selected in the Layers panel after creation [Taiga #8078](https://tree.taiga.io/project/penpot/issue/8078)
- Fix export boards loses opacity [Taiga #7592](https://tree.taiga.io/project/penpot/issue/7592)
- Fix show in view mode and interactions workflow [Taiga #4711](https://github.com/penpot/penpot/pull/4711)
## 2.0.3

View file

@ -6,4 +6,4 @@
(ns app.common.files.defaults)
(def version 48)
(def version 49)

View file

@ -937,6 +937,26 @@
(-> data
(update :pages-index update-vals update-page))))
(defn migrate-up-49
"Remove hide-in-viewer for shapes that are origin or destination of an interaction"
[data]
(letfn [(update-object [destinations object]
(cond-> object
(or (:interactions object)
(contains? destinations (:id object)))
(dissoc object :hide-in-viewer)))
(update-page [page]
(let [destinations (->> page
:objects
(vals)
(mapcat :interactions)
(map :destination)
(set))]
(update page :objects update-vals (partial update-object destinations))))]
(update data :pages-index update-vals update-page)))
(def migrations
"A vector of all applicable migrations"
[{:id 2 :migrate-up migrate-up-2}
@ -976,4 +996,5 @@
{:id 45 :migrate-up migrate-up-45}
{:id 46 :migrate-up migrate-up-46}
{:id 47 :migrate-up migrate-up-47}
{:id 48 :migrate-up migrate-up-48}])
{:id 48 :migrate-up migrate-up-48}
{:id 49 :migrate-up migrate-up-49}])

View file

@ -517,3 +517,16 @@
{:with-objects? true})
(pcb/reorder-grid-children [frame-id])))
(pcb/remove-objects empty-parents))))
(defn change-show-in-viewer [shape hide?]
(cond-> (assoc shape :hide-in-viewer hide?)
;; When a frame is no longer shown in view mode, it cannot have interactions
hide?
(dissoc :interactions)))
(defn add-new-interaction [shape interaction]
(-> shape
(update :interactions ctsi/add-interaction interaction)
;; When a interaction is created, the frame must be shown in view mode
(dissoc :hide-in-viewer)))

View file

@ -16,6 +16,7 @@
[app.common.types.pages-list :as ctpl]
[app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.typographies-list :as cttl]
[app.common.types.typography :as ctt]))
@ -99,3 +100,19 @@
[file label & {:keys [] :as params}]
(let [typography (sample-typography label params)]
(ctf/update-file-data file #(cttl/add-typography % typography))))
(defn add-interaction
[file origin-label dest-label]
(let [page (thf/current-page file)
origin (get-shape file origin-label)
dest (get-shape file dest-label)
interaction (-> ctsi/default-interaction
(ctsi/set-destination (:id dest))
(assoc :position-relative-to (:id origin)))
interactions (ctsi/add-interaction (:interactions origin) interaction)]
(ctf/update-file-data
file
(fn [file-data]
(ctpl/update-page file-data
(:id page)
#(ctst/set-shape % (assoc origin :interactions interactions)))))))

View file

@ -0,0 +1,75 @@
;; 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 common-tests.logic.hide-in-viewer-test
(:require
[app.common.files.changes-builder :as pcb]
[app.common.logic.shapes :as cls]
[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.shape.interactions :as ctsi]
[clojure.test :as t]))
(t/use-fixtures :each thi/test-fixture)
(t/deftest test-remove-show-in-view-mode-delete-interactions
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame :frame-dest)
(tho/add-frame :frame-origin)
(ths/add-interaction :frame-origin :frame-dest))
frame-origin (ths/get-shape file :frame-origin)
page (thf/current-page file)
;; ==== Action
changes (-> (pcb/empty-changes nil (:id page))
(pcb/with-objects (:objects page))
(pcb/update-shapes [(:id frame-origin)] #(cls/change-show-in-viewer % true)))
file' (thf/apply-changes file changes)
;; ==== Get
frame-origin' (ths/get-shape file' :frame-origin)]
;; ==== Check
(t/is (some? (:interactions frame-origin)))
(t/is (nil? (:interactions frame-origin')))))
(t/deftest test-add-new-interaction-updates-show-in-view-mode
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame :frame-dest :hide-in-viewer true)
(tho/add-frame :frame-origin :hide-in-viewer true))
frame-dest (ths/get-shape file :frame-dest)
frame-origin (ths/get-shape file :frame-origin)
page (thf/current-page file)
;; ==== Action
new-interaction (-> ctsi/default-interaction
(ctsi/set-destination (:id frame-dest))
(assoc :position-relative-to (:id frame-dest)))
changes (-> (pcb/empty-changes nil (:id page))
(pcb/with-objects (:objects page))
(pcb/update-shapes [(:id frame-origin)] #(cls/add-new-interaction % new-interaction)))
file' (thf/apply-changes file changes)
;; ==== Get
frame-origin' (ths/get-shape file' :frame-origin)]
;; ==== Check
(t/is (true? (:hide-in-viewer frame-origin)))
(t/is (nil? (:hide-in-viewer frame-origin')))))

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,222 @@
{
"~:features":{
"~#set":[
"layout/grid",
"styles/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 14",
"~:revn":1,
"~:modified-at":"~m1718088151182",
"~:id":"~u6191cd35-bb1f-81f7-8004-7cc63d087374",
"~:is-shared":false,
"~:version":48,
"~:project-id":"~u4dc640b0-5cbf-11ec-a7c5-91e9eb4f238d",
"~:created-at":"~m1718088142886",
"~:data":{
"~:pages":[
"~u6191cd35-bb1f-81f7-8004-7cc63d087375"
],
"~:pages-index":{
"~u6191cd35-bb1f-81f7-8004-7cc63d087375":{
"~: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":[
"~u7c75e310-c3a2-80fd-8004-7cc641479aef"
]
}
},
"~u7c75e310-c3a2-80fd-8004-7cc641479aef":{
"~#shape":{
"~:y":436,
"~:rx":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":126.00000000000006,
"~:type":"~:rect",
"~:points":[
{
"~#point":{
"~:x":266,
"~:y":436
}
},
{
"~#point":{
"~:x":392.00000000000006,
"~:y":436
}
},
{
"~#point":{
"~:x":392.00000000000006,
"~:y":570
}
},
{
"~#point":{
"~:x":266,
"~:y":570
}
}
],
"~: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":"~u7c75e310-c3a2-80fd-8004-7cc641479aef",
"~:parent-id":"~u00000000-0000-0000-0000-000000000000",
"~:frame-id":"~u00000000-0000-0000-0000-000000000000",
"~:strokes":[
],
"~:x":266,
"~:proportion":1,
"~:selrect":{
"~#rect":{
"~:x":266,
"~:y":436,
"~:width":126.00000000000006,
"~:height":134,
"~:x1":266,
"~:y1":436,
"~:x2":392.00000000000006,
"~:y2":570
}
},
"~:fills":[
{
"~:fill-color":"#B1B2B5",
"~:fill-opacity":1
}
],
"~:flip-x":null,
"~:ry":0,
"~:height":134,
"~:flip-y":null
}
}
},
"~:id":"~u6191cd35-bb1f-81f7-8004-7cc63d087375",
"~:name":"Page 1"
}
},
"~:id":"~u6191cd35-bb1f-81f7-8004-7cc63d087374"
}
}

File diff suppressed because one or more lines are too long

View file

@ -43,4 +43,18 @@ export class ViewerPage extends BaseWebSocketPage {
async cleanUp() {
await this.#ws.mockClose();
}
async showComments(clickOptions = {}) {
await this.page
.getByRole("button", { name: "Comments (G C)" })
.click(clickOptions);
}
async showCommentsThread(number, clickOptions = {}) {
await this.page
.getByTestId("floating-thread-bubble")
.filter({ hasText: number.toString() })
.click(clickOptions);
}
}

View file

@ -111,7 +111,11 @@ export class WorkspacePage extends BaseWebSocketPage {
const layer = this.layers.getByTestId("layer-item").filter({ has: this.page.getByText(name) });
await layer.getByRole("button").click(clickOptions);
}
async expectSelectedLayer(name) {
await expect(this.layers.getByTestId("layer-row").filter({ has: this.page.getByText(name) })).toHaveClass(/selected/);
}
async clickAssets(clickOptions = {}) {
await this.assets.click(clickOptions);
}

View file

@ -0,0 +1,34 @@
import { test, expect } from "@playwright/test";
import { ViewerPage } from "../pages/ViewerPage";
test.beforeEach(async ({ page }) => {
await ViewerPage.init(page);
});
const singleBoardFileId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb1";
const singleBoardPageId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb2";
const setupFileWithSingleBoard = async (viewer) => {
await viewer.mockRPC(/get\-view\-only\-bundle\?/, "viewer/get-view-only-bundle-single-board.json");
await viewer.mockRPC("get-comment-threads?file-id=*", "workspace/get-comment-threads-not-empty.json");
await viewer.mockRPC(
"get-file-fragment?file-id=*&fragment-id=*",
"viewer/get-file-fragment-single-board.json",
);
await viewer.mockRPC("get-comments?thread-id=*", "workspace/get-thread-comments.json");
await viewer.mockRPC("update-comment-thread-status", "workspace/update-comment-thread-status.json");
};
test("Comment is shown with scroll and valid position", async ({ page }) => {
const viewer = new ViewerPage(page);
await viewer.setupLoggedInUser();
await setupFileWithSingleBoard(viewer);
await viewer.goToViewer({ fileId: singleBoardFileId, pageId: singleBoardPageId });
await viewer.showComments();
await viewer.showCommentsThread(1);
await expect(viewer.page.getByRole("textbox", { name: "Reply" })).toBeVisible();
await viewer.showCommentsThread(1);
await viewer.showCommentsThread(2);
await expect(viewer.page.getByRole("textbox", { name: "Reply" })).toBeVisible();
});

View file

@ -66,3 +66,19 @@ test("User adds a library and its automatically selected in the color palette",
await expect(workspacePage.palette.getByText('There are no color styles in your library yet')).toBeVisible();
});
test("User makes a group", async ({ page }) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-not-empty.json");
await workspacePage.mockRPC("update-file?id=*", "workspace/update-file-create-rect.json");
await workspacePage.goToWorkspace({
fileId: "6191cd35-bb1f-81f7-8004-7cc63d087374",
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375"
});
await workspacePage.clickLeafLayer("Rectangle");
await workspacePage.page.keyboard.press("ControlOrMeta+g");
await workspacePage.expectSelectedLayer("Group");
});

Binary file not shown.

After

(image error) Size: 15 KiB

Binary file not shown.

After

(image error) Size: 1.2 MiB

Binary file not shown.

After

(image error) Size: 213 KiB

Binary file not shown.

After

(image error) Size: 243 KiB

Binary file not shown.

After

(image error) Size: 253 KiB

View file

@ -291,9 +291,7 @@
// INPUTS
.input-base {
@include removeInputStyle;
@include bodySmallTypography;
@include textEllipsis;
// @include focusInput;
height: $s-28;
width: 100%;
flex-grow: 1;
@ -326,7 +324,6 @@
}
.input-element {
@include bodySmallTypography;
@include focusInput;
display: flex;
align-items: center;

View file

@ -372,6 +372,7 @@
--pill-foreground-color: var(--color-foreground-primary);
--link-foreground-color: var(--color-accent-primary);
--register-confirmation-color: var(--status-color-success-200);
--resize-area-background-color: var(--color-background-primary);
--resize-area-border-color: var(--color-background-quaternary);

View file

@ -203,7 +203,7 @@
ptk/WatchEvent
(watch [_ state _]
(let [selected (wsh/lookup-selected state)]
(rx/of (group-shapes nil selected))))))
(rx/of (group-shapes nil selected :change-selection? true))))))
(defn ungroup-shapes
[ids & {:keys [change-selection?] :or {change-selection? false}}]

View file

@ -11,6 +11,7 @@
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt]
[app.common.logic.shapes :as cls]
[app.common.types.page :as ctp]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.interactions :as ctsi]
@ -131,8 +132,7 @@
(let [new-interaction (-> ctsi/default-interaction
(ctsi/set-destination destination)
(assoc :position-relative-to (:id shape)))]
(update shape :interactions
ctsi/add-interaction new-interaction)))))
(cls/add-new-interaction shape new-interaction)))))
(when (and (not (connected-frame? objects (:id frame)))
(nil? flow))
(rx/of (add-flow (:id frame))))))))))

View file

@ -97,7 +97,7 @@
(when cls
(cond
(true? v) cls
(false? v) nil
(false? v) ""
:else `(if ~v ~cls ""))))))
(interpose " ")))

View file

@ -15,7 +15,9 @@
[app.main.ui.frame-preview :as frame-preview]
[app.main.ui.icons :as i]
[app.main.ui.messages :as msgs]
[app.main.ui.onboarding :refer [onboarding-modal]]
[app.main.ui.onboarding.newsletter :refer [onboarding-newsletter]]
[app.main.ui.onboarding.questions :refer [questions-modal]]
[app.main.ui.onboarding.team-choice :refer [onboarding-team-modal]]
[app.main.ui.releases :refer [release-notes-modal]]
[app.main.ui.static :as static]
[app.util.dom :as dom]
@ -96,19 +98,43 @@
#_[:& app.main.ui.onboarding/onboarding-modal]
#_[:& app.main.ui.onboarding.team-choice/onboarding-team-modal]
(when-let [props (get profile :props)]
(cond
(and (not (:onboarding-viewed props))
(contains? cf/flags :onboarding))
[:& onboarding-modal {}]
(let [show-question-modal?
(and (contains? cf/flags :onboarding)
(not (:onboarding-viewed props))
(not (contains? props :onboarding-questions)))
(and (contains? cf/flags :onboarding)
(:onboarding-viewed props)
(not= (:release-notes-viewed props) (:main cf/version))
(not= "0.0" (:main cf/version)))
[:& release-notes-modal {:version (:main cf/version)}]))
show-newsletter-modal?
(and (contains? cf/flags :onboarding)
(not (:onboarding-viewed props))
(not (contains? props :newsletter-updates))
(contains? props :onboarding-questions))
show-team-modal?
(and (contains? cf/flags :onboarding)
(not (:onboarding-viewed props))
(not (contains? props :onboarding-team-id))
(contains? props :newsletter-updates))
show-release-modal?
(and (contains? cf/flags :onboarding)
(:onboarding-viewed props)
(not= (:release-notes-viewed props) (:main cf/version))
(not= "0.0" (:main cf/version)))]
(cond
show-question-modal?
[:& questions-modal]
show-newsletter-modal?
[:& onboarding-newsletter]
show-team-modal?
[:& onboarding-team-modal]
show-release-modal?
[:& release-notes-modal {:version (:main cf/version)}])))
[:& dashboard-page {:route route :profile profile}]]
:viewer
(let [{:keys [query-params path-params]} route
{:keys [index share-id section page-id interactions-mode frame-id]

View file

@ -44,6 +44,9 @@
{::mf/props :obj}
[{:keys [route]}]
(let [section (dm/get-in route [:data :name])
show-login-icon (and
(not= section :auth-register-validate)
(not= section :auth-register-success))
params (:query-params route)
error (:error params)]
@ -55,8 +58,9 @@
(st/emit! (du/show-redirect-error error))))
[:main {:class (stl/css :auth-section)}
[:h1 {:class (stl/css :logo-container)}
[:a {:href "#/" :title "Penpot" :class (stl/css :logo-btn)} i/logo]]
(when show-login-icon
[:h1 {:class (stl/css :logo-container)}
[:a {:href "#/" :title "Penpot" :class (stl/css :logo-btn)} i/logo]])
[:div {:class (stl/css :login-illustration)}
i/login-illustration]

View file

@ -31,6 +31,7 @@
display: flex;
justify-content: flex-start;
width: $s-120;
height: $s-96;
margin-block-end: $s-52;
}

View file

@ -10,14 +10,22 @@
width: 100%;
padding-block-end: 0;
display: grid;
gap: $s-24;
gap: $s-12;
form {
display: flex;
flex-direction: column;
gap: $s-12;
margin-top: $s-12;
}
}
.auth-title-wrapper {
width: 100%;
padding-block-end: 0;
display: grid;
gap: $s-8;
}
.separator {
border-color: var(--modal-separator-backogrund-color);
margin: 0;

View file

@ -169,7 +169,7 @@
[:& fm/input
{:name :email
:type "email"
:label (tr "auth.email")
:label (tr "auth.work-email")
:class (stl/css :form-field)}]]
[:div {:class (stl/css :fields-row)}

View file

@ -80,7 +80,7 @@
:form form}
[:div {:class (stl/css :fields-row)}
[:& fm/input {:name :email
:label (tr "auth.email")
:label (tr "auth.work-email")
:type "text"
:class (stl/css :form-field)}]]

View file

@ -104,7 +104,7 @@
[:div {:class (stl/css :fields-row)}
[:& fm/input {:type "text"
:name :email
:label (tr "auth.email")
:label (tr "auth.work-email")
:data-test "email-input"
:show-success? true
:class (stl/css :form-field)}]]
@ -134,11 +134,11 @@
(mf/defc register-page
{::mf/props :obj}
[{:keys [params]}]
[:div {:class (stl/css :auth-form-wrapper)}
[:div {:class (stl/css :auth-form-wrapper :register-form)}
[:h1 {:class (stl/css :auth-title)
:data-test "registration-title"} (tr "auth.register-title")]
[:p {:class (stl/css :auth-tagline)}
(tr "auth.login-tagline")]
(tr "auth.register-tagline")]
(when (contains? cf/flags :demo-warning)
[:& login/demo-warning])
@ -229,11 +229,11 @@
(mf/html
[:& tr-html
{:tag-name "div"
:label "auth.terms-privacy-agreement-md"
:label "auth.terms-and-privacy-agreement"
:params [cf/terms-of-service-uri cf/privacy-policy-uri]}])]
[:div {:class (stl/css :fields-row :input-visible :accept-terms-and-privacy-wrapper)}
[:& fm/input {:name :accept-terms-and-privacy
:class "check-primary"
:class (stl/css :checkbox-terms-and-privacy)
:type "checkbox"
:default-checked false
:label terms-label}]]))
@ -247,11 +247,12 @@
(mf/defc register-validate-page
[{:keys [params]}]
[:div {:class (stl/css :auth-form-wrapper)}
[:h1 {:class (stl/css :auth-title)
:data-test "register-title"} (tr "auth.register-title")]
[:div {:class (stl/css :auth-subtitle)} (tr "auth.register-subtitle")]
[:hr {:class (stl/css :separator)}]
[:h1 {:class (stl/css :logo-container)}
[:a {:href "#/" :title "Penpot" :class (stl/css :logo-btn)} i/logo]]
[:div {:class (stl/css :auth-title-wrapper)}
[:h2 {:class (stl/css :auth-title)
:data-test "register-title"} (tr "auth.register-account-title")]
[:div {:class (stl/css :auth-subtitle)} (tr "auth.register-account-tagline")]]
[:& register-validate-form {:params params}]
@ -264,7 +265,11 @@
(mf/defc register-success-page
[{:keys [params]}]
[:div {:class (stl/css :auth-form-wrapper :register-success)}
[:div {:class (stl/css :notification-icon)} i/icon-verify]
[:div {:class (stl/css :notification-text)} (tr "auth.verification-email-sent")]
[:h1 {:class (stl/css :logo-container)}
[:a {:href "#/" :title "Penpot" :class (stl/css :logo-btn)} i/logo]]
[:div {:class (stl/css :auth-title-wrapper)}
[:h2 {:class (stl/css :auth-title)}
(tr "auth.check-mail")]
[:div {:class (stl/css :notification-text)} (tr "auth.verification-email-sent")]]
[:div {:class (stl/css :notification-text-email)} (:email params "")]
[:div {:class (stl/css :notification-text)} (tr "auth.check-your-email")]])

View file

@ -8,15 +8,24 @@
@use "./common.scss";
.accept-terms-and-privacy-wrapper {
margin: $s-16 0;
:global(a) {
color: $df-secondary;
font-weight: $fw700;
}
}
.checkbox-terms-and-privacy {
align-items: flex-start;
}
.register-form {
gap: $s-24;
}
.register-success {
padding-bottom: $s-32;
gap: $s-24;
.auth-title {
@include medTitleTipography;
}
}
.notification-icon {
@ -30,9 +39,30 @@
}
}
.notification-text-email,
.notification-text {
font-size: $fs-16;
color: var(--notification-foreground-color-default);
margin-bottom: $s-16;
@include bodyMediumTypography;
color: var(--title-foreground-color);
}
.notification-text-email {
@include medTitleTipography;
font-size: $fs-20;
color: var(--register-confirmation-color);
margin-inline: $s-36;
}
.logo-btn {
height: $s-40;
svg {
width: $s-120;
height: $s-40;
fill: var(--main-icon-foreground);
}
}
.logo-container {
display: flex;
justify-content: flex-start;
width: $s-120;
margin-block-end: $s-24;
}

View file

@ -181,6 +181,7 @@
[:*
[:div
{:class (stl/css :floating-thread-bubble)
:data-testid "floating-thread-bubble"
:style {:top (str pos-y "px")
:left (str pos-x "px")}
:on-click dom/stop-propagation}
@ -435,9 +436,9 @@
[:* {:key (dm/str (:id item))}
[:& comment-item {:comment item
:users users
:origin origin}]])
[:div {:ref ref}]]
[:& reply-form {:thread thread}]])))
:origin origin}]])]
[:& reply-form {:thread thread}]
[:div {:ref ref}]])))
(defn use-buble
[zoom {:keys [position frame-id]}]
@ -558,6 +559,7 @@
:on-pointer-move on-pointer-move*
:on-click on-click*
:on-lost-pointer-capture on-lost-pointer-capture
:data-testid "floating-thread-bubble"
:class (stl/css-case
:floating-thread-bubble true
:resolved (:is-resolved thread)

View file

@ -236,6 +236,7 @@
.reply-form {
textarea {
@extend .input-element;
@include bodySmallTypography;
line-height: 1.45;
height: 100%;
width: 100%;

View file

@ -117,7 +117,7 @@
[:*
(cond
(some? label)
[:label {:class (stl/css-case :input-with-label (not is-checkbox?)
[:label {:class (stl/css-case :input-with-label-form (not is-checkbox?)
:input-label is-text?
:radio-label is-radio?
:checkbox-label is-checkbox?)
@ -214,7 +214,7 @@
[:span {:class (stl/css :hint)} hint])]))
(mf/defc select
[{:keys [options disabled form default dropdown-class] :as props
[{:keys [options disabled form default dropdown-class select-class] :as props
:or {default ""}}]
(let [input-name (get props :name)
form (or form (mf/use-ctx form-ctx))
@ -230,6 +230,7 @@
{:default-value value
:disabled disabled
:options options
:class select-class
:dropdown-class dropdown-class
:on-change handle-change}]]))
@ -297,6 +298,71 @@
:value value'
:checked checked?}]]))]))
(mf/defc image-radio-buttons
{::mf/wrap-props false}
[props]
(let [form (or (unchecked-get props "form")
(mf/use-ctx form-ctx))
name (unchecked-get props "name")
image (unchecked-get props "image")
img-height (unchecked-get props "img-height")
img-width (unchecked-get props "img-width")
current-value (or (dm/get-in @form [:data name] "")
(unchecked-get props "value"))
on-change (unchecked-get props "on-change")
options (unchecked-get props "options")
trim? (unchecked-get props "trim")
class (unchecked-get props "class")
encode-fn (d/nilv (unchecked-get props "encode-fn") identity)
decode-fn (d/nilv (unchecked-get props "decode-fn") identity)
on-change'
(mf/use-fn
(mf/deps on-change form name)
(fn [event]
(let [value (-> event dom/get-target dom/get-value decode-fn)]
(when (some? form)
(swap! form assoc-in [:touched name] true)
(fm/on-input-change form name value trim?))
(when (fn? on-change)
(on-change name value)))))]
[:div {:class (if image
class
(dm/str class " " (stl/css :custom-radio)))}
(for [{:keys [image icon value label area]} options]
(let [icon? (some? icon)
value' (encode-fn value)
checked? (= value current-value)
key (str/ffmt "%-%" (d/name name) (d/name value'))]
[:label {:for key
:key key
:style {:grid-area area}
:class (stl/css-case :radio-label-image true
:global/checked checked?)}
(cond
icon?
[:span {:class (stl/css :icon-inside)
:style {:height img-height
:width img-width}} icon]
:else
[:span {:style {:background-image (str/ffmt "url(%)" image)
:height img-height
:width img-width}
:class (stl/css :image-inside)}])
[:span {:class (stl/css :image-text)} label]
[:input {:on-change on-change'
:type "radio"
:class (stl/css :radio-input)
:id key
:name name
:value value'
:checked checked?}]]))]))
(mf/defc submit-button*
{::mf/wrap-props false}
[{:keys [on-click children label form class name disabled] :as props}]
@ -378,6 +444,7 @@
:no-padding (pos? (count @items))
:invalid (and (some? valid-item-fn)
touched?
(not (str/empty? @value))
(not (valid-item-fn @value)))))
on-focus

View file

@ -38,10 +38,9 @@
}
}
.input-with-label {
.input-with-label-form {
@include flexColumn;
gap: $s-8;
@include bodySmallTypography;
justify-content: flex-start;
align-items: flex-start;
height: 100%;
@ -55,6 +54,7 @@
color: var(--input-foreground-color-active);
margin-top: 0;
width: 100%;
max-width: 100%;
height: 100%;
padding: 0 $s-8;
@ -64,6 +64,7 @@
border-radius: $br-8;
}
}
// Input autofill
input:-webkit-autofill,
input:-webkit-autofill:hover,
@ -169,6 +170,10 @@
border-color: var(--input-checkbox-border-color-hover);
}
}
a {
// Need for terms and conditions links on register checkbox
color: var(--link-foreground-color);
}
}
}
@ -367,7 +372,7 @@
height: fit-content;
border-radius: $br-8;
padding: $s-8;
color: var(--input-foreground-color);
color: var(--input-foreground-color-rest);
border: $s-1 solid transparent;
&:focus,
&:focus-within {
@ -393,14 +398,12 @@
border-radius: $br-circle;
}
.radio-label.with-image {
.radio-label-image {
@include smallTitleTipography;
display: grid;
grid-template-rows: auto auto 0px;
justify-items: center;
gap: 0;
height: $s-116;
width: $s-92;
border-radius: $br-8;
margin: 0;
border: 1px solid var(--color-background-tertiary);
@ -413,22 +416,29 @@
outline: none;
border: $s-1 solid var(--input-border-color-active);
}
.image-text {
color: var(--input-foreground-color-rest);
display: grid;
align-self: center;
margin-bottom: $s-16;
padding-inline: $s-8;
text-align: center;
}
}
.image-inside {
width: $s-60;
height: $s-48;
background-size: $s-48;
margin: $s-16;
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
}
.icon-inside {
width: $s-60;
height: $s-48;
margin: $s-16;
@include flexCenter;
svg {
width: $s-60;
height: $s-48;
width: 40px;
height: 60px;
stroke: var(--icon-foreground);
fill: none;
}

View file

@ -34,6 +34,7 @@
.input-wrapper {
@extend .input-with-label;
@include bodySmallTypography;
}
.action-buttons {

View file

@ -81,6 +81,7 @@
}
.file-name-edit {
@extend .input-element;
@include bodySmallTypography;
flex-grow: 1;
}
.file-name-label {

View file

@ -540,5 +540,6 @@
.email-input {
@extend .input-base;
@include bodySmallTypography;
height: auto;
}

View file

@ -37,6 +37,7 @@
.group-name-input {
@extend .input-element-label;
@include bodySmallTypography;
margin-bottom: $s-8;
label {
@include flexColumn;

View file

@ -93,6 +93,7 @@
}
.input-wrapper {
@extend .input-element;
@include bodySmallTypography;
}
}
}

View file

@ -1,161 +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 INC
(ns app.main.ui.onboarding
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.config :as cf]
[app.main.data.events :as ev]
[app.main.data.modal :as modal]
[app.main.data.users :as du]
[app.main.store :as st]
[app.main.ui.onboarding.newsletter]
[app.main.ui.onboarding.questions]
[app.main.ui.onboarding.team-choice]
[app.main.ui.onboarding.templates]
[app.util.i18n :as i18n :refer [tr]]
[app.util.timers :as tm]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
;; --- ONBOARDING LIGHTBOX
(defn send-event
[event-name]
(st/emit! (ptk/event ::ev/event {::ev/name event-name
::ev/origin "dashboard"})))
(mf/defc onboarding-welcome
[{:keys [next] :as props}]
(let [go-next
(fn []
(send-event "onboarding-step1-continue")
(next))]
[:div {:class (stl/css :modal-container)}
[:div {:class (stl/css :modal-left)}
[:img {:src "images/welcomeilustration.svg"
:border "0"
:alt (tr "onboarding.welcome.alt")}]]
[:div {:class (stl/css :modal-right)}
[:div {:class (stl/css :release)}
"Version " (:main cf/version)]
[:h1 {:class (stl/css :modal-title)
:data-test "onboarding-welcome"}
(tr "onboarding-v2.welcome.title")]
[:p {:class (stl/css :modal-text)}
(tr "onboarding-v2.welcome.desc1")]
[:div {:class (stl/css :text-wrapper)}
[:div {:class (stl/css :property-title)}
[:a {:href "https://community.penpot.app/"
:target "_blank"
:on-click #(send-event "onboarding-community-link")}
(tr "onboarding-v2.welcome.desc2.title")]]
[:div {:class (stl/css :property-description)}
(tr "onboarding-v2.welcome.desc2")]]
[:div {:class (stl/css :text-wrapper)}
[:div {:class (stl/css :property-title)}
[:a {:href "https://help.penpot.app/contributing-guide/"
:target "_blank" :on-click #(send-event "onboarding-contributing-link")}
(tr "onboarding-v2.welcome.desc3.title")]]
[:div {:class (stl/css :property-description)}
(tr "onboarding-v2.welcome.desc3")]]
[:button {:on-click go-next
:class (stl/css :accept-btn)
:data-test "onboarding-next-btn"}
(tr "labels.continue")]]]))
(mf/defc onboarding-before-start
[{:keys [next] :as props}]
(let [go-next
(fn []
(send-event "onboarding-step2-continue")
(next))]
[:div {:class (stl/css :modal-container)}
[:div {:class (stl/css :modal-left)}
[:img {:src "images/beforeyoustartilustration.svg"
:border "0"
:alt (tr "onboarding.welcome.alt")}]]
[:div {:class (stl/css :modal-right)}
[:div {:class (stl/css :release)}
"Version " (:main cf/version)]
[:h1 {:class (stl/css :modal-title)
:data-test "onboarding-welcome"}
(tr "onboarding-v2.before-start.title")]
[:p {:class (stl/css :modal-text)}
(tr "onboarding-v2.before-start.desc1")]
[:div {:class (stl/css :text-wrapper)}
[:div {:class (stl/css :property-title)}
[:a {:class (stl/css :modal-link)
:href "https://help.penpot.app/user-guide/"
:target "_blank"
:on-click #(send-event "onboarding-user-guide-link")}
(tr "onboarding-v2.before-start.desc2.title")]]
[:div {:class (stl/css :property-description)}
(tr "onboarding-v2.before-start.desc2")]]
[:div {:class (stl/css :text-wrapper)}
[:div {:class (stl/css :property-title)}
[:a {:class (stl/css :modal-link)
:href "https://www.youtube.com/c/Penpot"
:target "_blank"
:on-click #(send-event "onboarding-video-tutorials-link")}
(tr "onboarding-v2.before-start.desc3.title")]]
[:div {:class (stl/css :property-description)}
(tr "onboarding-v2.before-start.desc3")]]
[:button {:on-click go-next
:class (stl/css :accept-btn)
:data-test "onboarding-next-btn"}
(tr "labels.continue")]]]))
(mf/defc onboarding-modal
{::mf/register modal/components
::mf/register-as :onboarding}
[_]
(let [slide (mf/use-state :start)
klass (mf/use-state "fadeInDown")
navigate
(mf/use-fn #(reset! slide %))
skip
(mf/use-fn
(fn []
(st/emit! (modal/hide)
(du/mark-onboarding-as-viewed))
(cond
(contains? cf/flags :onboarding-questions)
(modal/show! {:type :onboarding-questions})
(contains? cf/flags :onboarding-newsletter)
(modal/show! {:type :onboarding-newsletter})
(contains? cf/flags :onboarding-team)
(modal/show! {:type :onboarding-team}))))
onboarding-a-b-test? (cf/external-feature-flag "signup-background" "test")]
(mf/with-effect [@slide]
(when (not= :start @slide)
(reset! klass "fadeIn"))
(let [sem (tm/schedule 300 #(reset! klass nil))]
(fn []
(reset! klass nil)
(tm/dispose! sem))))
[:div {:class (stl/css-case :modal-overlay true
:onboarding-a-b-test onboarding-a-b-test?)}
[:div.animated {:class (dm/str @klass " " (stl/css :animated))}
(case @slide
:start [:& onboarding-welcome {:next #(navigate :opensource)}]
:opensource [:& onboarding-before-start {:next skip}])]]))

View file

@ -1,86 +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 INC
@import "refactor/common-refactor.scss";
.modal-overlay {
@extend .modal-overlay-base;
}
.modal-container {
@extend .modal-container-base;
position: relative;
display: grid;
grid-template-columns: auto auto;
gap: $s-32;
padding-inline: $s-100;
padding-block-start: $s-100;
padding-block-end: $s-72;
margin: 0;
width: $s-960;
height: $s-632;
max-width: $s-960;
max-height: $s-632;
}
.modal-left {
width: $s-240;
margin-block-end: $s-64;
img {
width: $s-240;
height: 100%;
border-radius: $br-8 0 0 $br-8;
}
}
.modal-right {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: $s-40 auto auto auto $s-32;
gap: $s-24;
position: relative;
}
.release {
@include bodySmallTypography;
position: absolute;
top: calc(-1 * $s-28);
right: 0;
padding: $s-8;
color: var(--modal-text-foreground-color);
}
.modal-title {
@include bigTitleTipography;
color: var(--modal-title-foreground-color);
}
.modal-text,
.property-description {
@include bodyLargeTypography;
margin: 0;
color: var(--modal-text-foreground-color);
}
.modal-link {
@include bodyLargeTypography;
color: var(--modal-link-foreground-color);
margin: 0;
}
.text-wrapper {
@include flexColumn;
}
.property-title a {
@include medTitleTipography;
color: var(--modal-title-foreground-color);
}
.accept-btn {
@extend .modal-accept-btn;
justify-self: flex-end;
}

View file

@ -8,39 +8,52 @@
(:require-macros [app.main.style :as stl])
(:require
[app.config :as cf]
[app.main.data.events :as-alias ev]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.data.users :as du]
[app.main.store :as st]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
(mf/defc onboarding-newsletter
{::mf/register modal/components
::mf/register-as :onboarding-newsletter}
[]
(let [message (tr "onboarding.newsletter.acceptance-message")
newsletter-updates (mf/use-state false)
newsletter-news (mf/use-state false)
toggle
(mf/use-callback
(fn [option]
(swap! option not)))
(let [state* (mf/use-state #(do {:newsletter-updates false
:newsletter-news false}))
state (deref state*)
on-change
(mf/use-fn
(fn [event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "attr")
(keyword))]
(swap! state* update attr not))))
accept
(mf/use-callback
(mf/deps @newsletter-updates @newsletter-news)
on-next
(mf/use-fn
(mf/deps state)
(fn []
(st/emit! (when (or @newsletter-updates @newsletter-news)
(msg/success message))
(modal/show {:type :onboarding-team})
(du/update-profile-props {:newsletter-updates @newsletter-updates :newsletter-news @newsletter-news}))))
onboarding-a-b-test? (cf/external-feature-flag "signup-background" "test")]
(when (or (:newsletter-updates state)
(:newsletter-news state))
(st/emit! (msg/success (tr "onboarding.newsletter.acceptance-message"))))
(let [params (-> state
(assoc ::ev/name "onboarding-step")
(assoc :label "newsletter:subscriptions")
(assoc :step 6))]
(st/emit! (ptk/data-event ::ev/event params)
(du/update-profile-props state)))))
onboarding-a-b-test?
(cf/external-feature-flag "signup-background" "test")]
[:div {:class (stl/css-case
:modal-overlay true
:onboarding-a-b-test onboarding-a-b-test?)}
[:div {:class (stl/css-case :modal-overlay true
:onboarding-a-b-test onboarding-a-b-test?)}
[:div.animated.fadeInDown {:class (stl/css :modal-container)}
[:div {:class (stl/css :modal-left)}
[:img {:src "images/deco-newsletter.png"
@ -50,30 +63,34 @@
[:h2 {:class (stl/css :modal-title)
:data-test "onboarding-newsletter-title"}
(tr "onboarding.newsletter.title")]
[:p {:class (stl/css :modal-text)}
(tr "onboarding-v2.newsletter.desc")]
[:div {:class (stl/css :newsletter-options)}
[:div {:class (stl/css :input-wrapper)}
[:label {:for "newsletter-updates"}
[:span {:class (stl/css-case :global/checked @newsletter-updates)}
(when @newsletter-updates
i/status-tick)]
[:span {:class (stl/css-case :global/checked (:newsletter-updates state))}
i/status-tick]
(tr "onboarding-v2.newsletter.updates")
[:input {:type "checkbox"
:id "newsletter-updates"
:on-change #(toggle newsletter-updates)}]]]
:data-attr "newsletter-updates"
:value (:newsletter-updates state)
:on-change on-change}]]]
[:div {:class (stl/css :input-wrapper)}
[:label {:for "newsletter-news"}
[:span {:class (stl/css-case :global/checked @newsletter-news)}
(when @newsletter-news
i/status-tick)]
[:span {:class (stl/css-case :global/checked (:newsletter-news state))}
i/status-tick]
(tr "onboarding-v2.newsletter.news")
[:input {:type "checkbox"
:id "newsletter-news"
:on-change #(toggle newsletter-news)}]]]]
:data-attr "newsletter-news"
:value (:newsletter-news state)
:on-change on-change}]]]]
[:p {:class (stl/css :modal-text)}
(tr "onboarding-v2.newsletter.privacy1")
@ -84,5 +101,6 @@
[:p {:class (stl/css :modal-text)}
(tr "onboarding-v2.newsletter.privacy2")]
[:button {:on-click accept
:class (stl/css :accept-btn)} (tr "labels.continue")]]]]))
[:button {:on-click on-next
:class (stl/css :accept-btn)}
(tr "labels.continue")]]]]))

View file

@ -11,7 +11,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.config :as cf]
[app.main.data.modal :as modal]
[app.main.data.events :as-alias ev]
[app.main.data.users :as du]
[app.main.store :as st]
[app.main.ui.components.forms :as fm]
@ -19,223 +19,430 @@
[app.util.i18n :as i18n :refer [tr]]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
(mf/defc step-container
[{:keys [form step on-next on-prev children class] :as props}]
{::mf/props :obj}
[{:keys [form step on-next on-prev children class label]}]
[:& fm/form {:form form :on-submit on-next :class (dm/str class " " (stl/css :form-wrapper))}
[:div {:class (stl/css :paginator)} (str/ffmt "%/4" step)]
(let [on-next*
(mf/use-fn
(mf/deps on-next step label)
(fn [form event]
(let [params (-> (:clean-data @form)
(assoc :label label)
(assoc :step step)
(assoc ::ev/name "onboarding-step"))]
(st/emit! (ptk/data-event ::ev/event params))
(on-next form event))))]
children
[:& fm/form {:form form
:on-submit on-next*
:class (dm/str class " " (stl/css :form-wrapper))}
[:div {:class (stl/css :paginator)} (str/ffmt "%/5" step)]
[:div {:class (stl/css :action-buttons)}
children
(when on-prev
[:button {:class (stl/css :prev-button)
:on-click on-prev} (tr "questions.previous")])
[:div {:class (stl/css :action-buttons)}
[:> fm/submit-button*
{:label (if (< step 4) (tr "questions.next") (tr "questions.start"))
:class (stl/css :next-button)}]]])
(when (some? on-prev)
[:button {:class (stl/css :prev-button)
:on-click on-prev}
(tr "questions.previous")])
[:> fm/submit-button*
{:label (if (< step 5)
(tr "questions.next")
(tr "questions.start"))
:class (stl/css :next-button)}]]]))
(s/def ::questions-form-step-1
(s/keys :req-un [::planning]))
(mf/defc step-1
[{:keys [on-next form] :as props}]
[:& step-container {:form form :step 1 :on-next on-next :class (stl/css :step-1)}
[:img {:class (stl/css :header-image)
:src "images/form/use-for-1.png" :alt (tr "questions.lets-get-started")}]
[:h1 {:class (stl/css :modal-title)} (tr "questions.lets-get-started")]
[:p {:class (stl/css :modal-text)} (tr "questions.your-feedback-will-help-us")]
[:div {:class (stl/css :modal-question)}
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.questions-how-are-you-planning-to-use-penpot")]
[:& fm/select
{:options [{:label (tr "questions.select-option")
:value "" :key "questions-how-are-you-planning-to-use-penpot"
:disabled true}
{:label (tr "questions.discover-more-about-penpot")
:value "discover-more-about-penpot"
:key "discover-more-about-penpot"}
{:label (tr "questions.test-penpot-to-see-if-its-a-fit-for-team")
:value "test-penpot-to-see-if-its-a-fit-for-team"
:key "test-penpot-to-see-if-its-a-fit-for-team"}
{:label (tr "questions.start-to-work-on-my-project")
:value "start-to-work-on-my-project"
:key "start-to-work-on-my-project"}
{:label (tr "questions.get-the-code-from-my-team-project")
:value "get-the-code-from-my-team-project"
:key "get-the-code-from-my-team-project"}
{:label (tr "questions.leave-feedback-for-my-team-project")
:value "leave-feedback-for-my-team-project"
:key "leave-feedback-for-my-team-project"}
{:label (tr "questions.work-in-concept-ideas")
:value "work-in-concept-ideas"
:key "work-in-concept-ideas"}
{:label (tr "questions.try-out-before-using-penpot-on-premise")
:value "try-out-before-using-penpot-on-premise"
:key "try-out-before-using-penpot-on-premise"}]
:default ""
:name :planning
:dropdown-class (stl/css :question-dropdown)}]]])
(s/def ::questions-form-step-2
(s/keys :req-un [::experience-branding-illustrations-marketing-pieces
::experience-interface-design-visual-assets-design-systems
::experience-interface-wireframes-user-journeys-flows-navigation-trees]))
(mf/defc step-2
[{:keys [on-next on-prev form] :as props}]
[:& step-container {:form form :step 2 :on-next on-next :on-prev on-prev :class (stl/css :step-2)}
[:h1 {:class (stl/css :modal-title)}
(tr "questions.describe-your-experience-working-on")]
[:div {:class (stl/css-case :modal-question true
:question-centered true)}
[:div {:class (stl/css-case :modal-subtitle true
:centered true)}
(tr "branding-illustrations-marketing-pieces")]
[:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"}
{:label (tr "questions.some") :value "some"}
{:label (tr "questions.a-lot") :value "a-lot"}]
:name :experience-branding-illustrations-marketing-pieces
:class (stl/css :radio-btns)}]]
[:div {:class (stl/css-case :modal-question true
:question-centered true)}
[:div {:class (stl/css-case :modal-subtitle true
:centered true)}
(tr "questions.interface-design-visual-assets-design-systems")]
[:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"}
{:label (tr "questions.some") :value "some"}
{:label (tr "questions.a-lot") :value "a-lot"}]
:name :experience-interface-design-visual-assets-design-systems
:class (stl/css :radio-btns)}]]
[:div {:class (stl/css-case :modal-question true
:question-centered true)}
[:div {:class (stl/css-case :modal-subtitle true
:centered true)}
(tr "questions.wireframes-user-journeys-flows-navigation-trees")]
[:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"}
{:label (tr "questions.some") :value "some"}
{:label (tr "questions.a-lot") :value "a-lot"}]
:name :experience-interface-wireframes-user-journeys-flows-navigation-trees
:class (stl/css :radio-btns)}]]])
(s/def ::questions-form-step-3
(s/keys :req-un [::experience-design-tool]
:opt-un [::experience-design-tool-other]))
(s/keys :req-un [::planning
::expected-use]
:opt-un [::planning-other]))
(defn- step-1-form-validator
[errors data]
(let [planning (-> (:planning data) (str/trim))]
(let [planning (:planning data)
planning-other (:planning-other data)]
(cond-> errors
(= planning "")
(and (= planning "other")
(str/blank? planning-other))
(assoc :planning-other {:code "missing"})
(not= planning "other")
(assoc :planning-other nil)
(str/blank? planning)
(assoc :planning {:code "missing"}))))
(mf/defc step-1
{::mf/props :obj}
[{:keys [on-next form]}]
(let [use-options
(mf/with-memo []
(shuffle [{:label (tr "questions.use-work") :value "use-work"}
{:label (tr "questions.use-education") :value "use-education"}
{:label (tr "questions.use-personal") :value "use-personal"}]))
planning-options
(mf/with-memo []
(-> (shuffle [{:label (tr "questions.select-option")
:value "" :key "questions:what-brings-you-here"
:disabled true}
{:label (tr "questions.reasons.exploring")
:value "discover-more-about-penpot"
:key "discover-more-about-penpot"}
{:label (tr "questions.reasons.fit")
:value "test-penpot-to-see-if-its-a-fit-for-team"
:key "test-penpot-to-see-if-its-a-fit-for-team"}
{:label (tr "questions.reasons.alternative")
:value "alternative-to-figma"
:key "alternative-to-figma"}
{:label (tr "questions.reasons.testing")
:value "try-out-before-using-penpot-on-premise"
:key "try-out-before-using-penpot-on-premise"}])
(conj {:label (tr "questions.other-short") :value "other"})))
current-planning
(dm/get-in @form [:data :planning])]
[:& step-container {:form form
:step 1
:label "questions:about-you"
:on-next on-next
:class (stl/css :step-1)}
[:img {:class (stl/css :header-image)
:src "images/form/use-for-1.png"
:alt (tr "questions.lets-get-started")}]
[:h1 {:class (stl/css :modal-title)}
(tr "questions.step1-title")]
[:p {:class (stl/css :modal-text)}
(tr "questions.step1-subtitle")]
[:div {:class (stl/css :modal-question)}
[:h3 {:class (stl/css :modal-subtitle)}
(tr "questions.step1-question1")]
[:& fm/radio-buttons {:options use-options
:name :expected-use
:class (stl/css :radio-btns)}]
[:h3 {:class (stl/css :modal-subtitle)}
(tr "questions.step1-question2")]
[:& fm/select
{:options planning-options
:select-class (stl/css :select-class)
:default ""
:name :planning
:dropdown-class (stl/css :question-dropdown)}]
(when (= current-planning "other")
[:& fm/input {:name :planning-other
:class (stl/css :input-spacing)
:placeholder (tr "questions.other")
:label ""}])]]))
(s/def ::questions-form-step-2
(s/keys :req-un [::experience-design-tool]
:opt-un [::experience-design-tool-other]))
(defn- step-2-form-validator
[errors data]
(let [experience (:experience-design-tool data)
experience-other (:experience-design-tool-other data)]
(cond-> errors
(and (= experience "other")
(str/blank? experience-other))
(assoc :experience-design-tool-other {:code "missing"})
(not= experience "other")
(assoc :experience-design-tool-other nil))))
(mf/defc step-2
{::mf/props :obj}
[{:keys [on-next on-prev form]}]
(let [design-tool-options
(mf/with-memo []
(-> (shuffle [{:label (tr "questions.figma") :img-width "48px" :img-height "60px"
:value "figma" :image "images/form/figma.png"}
{:label (tr "questions.sketch") :img-width "48px" :img-height "60px"
:value "sketch" :image "images/form/sketch.png"}
{:label (tr "questions.adobe-xd") :img-width "48px" :img-height "60px"
:value "adobe-xd" :image "images/form/adobe-xd.png"}
{:label (tr "questions.canva") :img-width "48px" :img-height "60px"
:value "canva" :image "images/form/canva.png"}
{:label (tr "questions.invision") :img-width "48px" :img-height "60px"
:value "invision" :image "images/form/invision.png"}])
(conj {:label (tr "questions.other-short") :value "other" :icon i/curve})))
current-experience
(dm/get-in @form [:clean-data :experience-design-tool])
on-design-tool-change
(mf/use-fn
(mf/deps current-experience)
(fn []
(when (not= current-experience "other")
(swap! form d/dissoc-in [:data :experience-design-tool-other])
(swap! form d/dissoc-in [:errors :experience-design-tool-other]))))]
[:& step-container {:form form
:step 2
:label "questions:experience-design-tool"
:on-next on-next
:on-prev on-prev
:class (stl/css :step-2)}
[:h1 {:class (stl/css :modal-title)}
(tr "question.design-tool-more-used")]
[:div {:class (stl/css :radio-wrapper)}
[:& fm/image-radio-buttons {:options design-tool-options
:img-width "48px"
:img-height "60px"
:name :experience-design-tool
:image true
:class (stl/css :image-radio)
:on-change on-design-tool-change}]
(when (= current-experience "other")
[:& fm/input {:name :experience-design-tool-other
:class (stl/css :input-spacing)
:placeholder (tr "questions.other")
:label ""}])]]))
(s/def ::questions-form-step-3
(s/keys :req-un [::team-size ::role ::responsability]
:opt-un [::role-other ::responsability-other]))
(defn- step-3-form-validator
[errors data]
(let [experience-design-tool (:experience-design-tool data)
experience-design-tool-other (-> (:experience-design-tool-other data) str/trim)]
(let [role (:role data)
role-other (:role-other data)
responsability (:responsability data)
responsability-other (:responsability-other data)]
(cond-> errors
(and (= experience-design-tool "other") (= 0 (count experience-design-tool-other)))
(assoc :experience-design-tool-other {:code "missing"}))))
(and (= role "other")
(str/blank? role-other))
(assoc :role-other {:code "missing"})
(not= role "other")
(assoc :role-other nil)
(and (= responsability "other")
(str/blank? responsability-other))
(assoc :responsability-other {:code "missing"})
(not= responsability "other")
(assoc :responsability-other nil))))
(mf/defc step-3
[{:keys [on-next on-prev form] :as props}]
(let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])
on-design-tool-change
(fn [_ _]
(let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])]
(when (not= experience-design-tool "other")
(do
(swap! form d/dissoc-in [:data :experience-design-tool-other])
(swap! form d/dissoc-in [:errors :experience-design-tool-other])))))]
{::mf/props :obj}
[{:keys [on-next on-prev form]}]
(let [role-options
(mf/with-memo []
(-> (shuffle [{:label (tr "questions.select-option") :value "" :key "role" :disabled true}
{:label (tr "questions.work-type.ux") :value "designer" :key "designer"}
{:label (tr "questions.work-type.dev") :value "developer" :key "developer"}
{:label (tr "questions.work-type.student") :value "student-teacher" :key "student"}
{:label (tr "questions.work-type.graphic") :value "graphic-design" :key "design"}
{:label (tr "questions.work-type.marketing") :value "marketing" :key "marketing"}
{:label (tr "questions.work-type.product") :value "manager" :key "manager"}])
(conj {:label (tr "questions.other-short") :value "other"})))
[:& step-container {:form form :step 3 :on-next on-next :on-prev on-prev :class (stl/css :step-3)}
[:h1 {:class (stl/css :modal-title)}
(tr "question.design-tool-more-experienced-with")]
[:div {:class (stl/css :radio-wrapper)}
[:& fm/radio-buttons {:options [{:label (tr "questions.figma") :value "figma" :image "images/form/figma.png" :area "image1"}
{:label (tr "questions.sketch") :value "sketch" :image "images/form/sketch.png" :area "image2"}
{:label (tr "questions.adobe-xd") :value "adobe-xd" :image "images/form/adobe-xd.png" :area "image3"}
{:label (tr "questions.canva") :value "canva" :image "images/form/canva.png" :area "image4"}
{:label (tr "questions.invision") :value "invision" :image "images/form/invision.png" :area "image5"}
{:label (tr "questions.never-used-one") :area "image6" :value "never-used-a-tool" :icon i/curve}
{:label (tr "questions.other") :value "other" :area "other"}]
:name :experience-design-tool
:image true
:class (stl/css :image-radio)
:on-change on-design-tool-change}]
responsability-options
(mf/with-memo []
(-> (shuffle [{:label (tr "questions.select-option") :value "" :key "responsability" :disabled true}
{:label (tr "questions.role.team-leader") :value "team-leader"}
{:label (tr "questions.role.team-member") :value "team-member"}
{:label (tr "questions.role.freelancer") :value "freelancer"}
{:label (tr "questions.role.founder") :value "ceo-founder"}
{:label (tr "questions.role.director") :value "director"}
{:label (tr "questions.student-teacher") :value "student-teacher"}])
(conj {:label (tr "questions.other-short") :value "other"})))
[:& fm/input {:name :experience-design-tool-other
:class (stl/css :input-spacing)
:placeholder (tr "questions.other")
:label ""
:disabled (not= experience-design-tool "other")}]]]))
team-size-options
(mf/with-memo []
[{:label (tr "questions.select-option") :value "" :key "team-size" :disabled true}
{:label (tr "questions.more-than-50") :value "more-than-50" :key "more-than-50"}
{:label (tr "questions.31-50") :value "31-50" :key "31-50"}
{:label (tr "questions.11-30") :value "11-30" :key "11-30"}
{:label (tr "questions.2-10") :value "2-10" :key "2-10"}
{:label (tr "questions.freelancer") :value "freelancer" :key "freelancer"}
{:label (tr "questions.personal-project") :value "personal-project" :key "personal-project"}])
current-role
(dm/get-in @form [:data :role])
current-responsability
(dm/get-in @form [:data :responsability])]
[:& step-container {:form form
:step 3
:label "questions:about-your-job"
:on-next on-next
:on-prev on-prev
:class (stl/css :step-3)}
[:h1 {:class (stl/css :modal-title)} (tr "questions.step3-title")]
[:div {:class (stl/css :modal-question)}
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.step3.question1")]
[:& fm/select {:options role-options
:select-class (stl/css :select-class)
:default ""
:name :role}]
(when (= current-role "other")
[:& fm/input {:name :role-other
:class (stl/css :input-spacing)
:placeholder (tr "questions.other")
:label ""}])]
[:div {:class (stl/css :modal-question)}
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.step3.question2")]
[:& fm/select {:options responsability-options
:select-class (stl/css :select-class)
:default ""
:name :responsability}]
(when (= current-responsability "other")
[:& fm/input {:name :responsability-other
:class (stl/css :input-spacing)
:placeholder (tr "questions.other")
:label ""}])]
[:div {:class (stl/css :modal-question)}
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.company-size")]
[:& fm/select {:options team-size-options
:default ""
:select-class (stl/css :select-class)
:name :team-size}]]]))
(s/def ::questions-form-step-4
(s/keys :req-un [::team-size ::role]
:opt-un [::role-other]))
(s/keys :req-un [::start-with]
:opt-un [::start-with-other]))
(defn- step-4-form-validator
[errors data]
(let [role (:role data)
role-other (-> (:role-other data) str/trim)]
(let [start (:start-with data)
start-other (:start-with-other data)]
(cond-> errors
(and (= role "other") (= 0 (count role-other)))
(assoc :role-other {:code "missing"}))))
(and (= start "other")
(str/blank? start-other))
(assoc :start-with-other {:code "missing"})
(not= start "other")
(assoc :start-with-other nil))))
(mf/defc step-4
[{:keys [on-next on-prev form] :as props}]
(let [role (dm/get-in @form [:data :role])
on-role-change
(fn [_ _]
(let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])]
(when (not= experience-design-tool "other")
(do
(swap! form d/dissoc-in [:data :role-other])
(swap! form d/dissoc-in [:errors :role-other])))))]
{::mf/props :obj}
[{:keys [on-next on-prev form]}]
(let [start-options
(mf/with-memo []
(-> (shuffle [{:label (tr "questions.starting-ui") :value "ui" :image "images/form/Design.png"}
{:label (tr "questions.starting-wireframing") :value "wireframing" :image "images/form/templates.png"}
{:label (tr "questions.starting-prototyping") :value "prototyping" :image "images/form/Prototype.png"}
{:label (tr "questions.starting-ds") :value "ds" :image "images/form/components.png"}
{:label (tr "questions.starting-code") :value "code" :image "images/form/design-and-dev.png"}])
(conj {:label (tr "questions.other-short") :value "other" :icon i/curve})))
[:& step-container {:form form :step 4 :on-next on-next :on-prev on-prev :class (stl/css :step-4)}
[:h1 {:class (stl/css :modal-title)} (tr "questions.role")]
current-start (dm/get-in @form [:data :start-with])
on-start-change
(mf/use-fn
(mf/deps current-start)
(fn [_ _]
(when (not= current-start "other")
(swap! form d/dissoc-in [:data :start-with-other])
(swap! form d/dissoc-in [:errors :start-with-other]))))]
[:& step-container {:form form
:step 4
:label "questions:how-start"
:on-next on-next
:on-prev on-prev
:class (stl/css :step-4)}
[:h1 {:class (stl/css :modal-title)} (tr "questions.step4-title")]
[:div {:class (stl/css :radio-wrapper)}
[:& fm/radio-buttons {:options [{:label (tr "questions.designer") :value "designer"}
{:label (tr "questions.developer") :value "developer"}
{:label (tr "questions.manager") :value "manager"}
{:label (tr "questions.founder") :value "founder"}
{:label (tr "questions.marketing") :value "marketing"}
{:label (tr "questions.student-teacher") :value "student-teacher"}
{:label (tr "questions.other") :value "other"}]
:name :role
:on-change on-role-change}]
[:& fm/input {:name :role-other
:class (stl/css :input-spacing)
:label ""
:placeholder (tr "questions.other")
:disabled (not= role "other")}]]
[:& fm/image-radio-buttons {:options start-options
:img-width "159px"
:img-height "120px"
:class (stl/css :image-radio)
:name :start-with
:on-change on-start-change}]
[:div {:class (stl/css :modal-question)}
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.team-size")]
[:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "team-size" :disabled true}
{:label (tr "questions.more-than-50") :value "more-than-50" :key "more-than-50"}
{:label (tr "questions.31-50") :value "31-50" :key "31-50"}
{:label (tr "questions.11-30") :value "11-30" :key "11-30"}
{:label (tr "questions.2-10") :value "2-10" :key "2-10"}
{:label (tr "questions.freelancer") :value "freelancer" :key "freelancer"}
{:label (tr "questions.personal-project") :value "personal-project" :key "personal-project"}]
:default ""
:name :team-size}]]]))
(when (= current-start "other")
[:& fm/input {:name :start-with-other
:class (stl/css :input-spacing)
:label ""
:placeholder (tr "questions.other")}])]]))
;; NOTE: we don't register it on registry modal because we reference
;; this modal directly on the ui namespace.
(s/def ::questions-form-step-5
(s/keys :req-un [::referer]
:opt-un [::referer-other]))
(defn- step-5-form-validator
[errors data]
(let [referer (:referer data)
referer-other (:referer-other data)]
(cond-> errors
(and (= referer "other")
(str/blank? referer-other))
(assoc :referer-other {:code "missing"})
(not= referer "other")
(assoc :referer-other nil))))
(mf/defc step-5
{::mf/props :obj}
[{:keys [on-next on-prev form]}]
(let [referer-options
(mf/with-memo []
(-> (shuffle [{:label (tr "questions.referer.youtube") :value "youtube"}
{:label (tr "questions.referer.event") :value "event"}
{:label (tr "questions.referer.search") :value "search"}
{:label (tr "questions.referer.social") :value "social"}
{:label (tr "questions.referer.article") :value "article"}])
(conj {:label (tr "questions.other-short") :value "other"})))
current-referer
(dm/get-in @form [:data :referer])
on-referer-change
(mf/use-fn
(mf/deps current-referer)
(fn []
(when (not= current-referer "other")
(swap! form d/dissoc-in [:data :referer-other])
(swap! form d/dissoc-in [:errors :referer-other]))))]
[:& step-container {:form form
:step 5
:label "questions:referer"
:on-next on-next
:on-prev on-prev
:class (stl/css :step-5)}
[:h1 {:class (stl/css :modal-title)} (tr "questions.step5-title")]
[:div {:class (stl/css :radio-wrapper)}
[:& fm/radio-buttons {:options referer-options
:class (stl/css :radio-btns)
:name :referer
:on-change on-referer-change}]
(when (= current-referer "other")
[:& fm/input {:name :referer-other
:class (stl/css :input-spacing)
:label ""
:placeholder (tr "questions.other")}])]]))
(mf/defc questions-modal
{::mf/register modal/components
::mf/register-as :onboarding-questions}
[]
(let [container (mf/use-ref)
step (mf/use-state 1)
@ -247,9 +454,12 @@
:initial {}
:validators [step-1-form-validator]
:spec ::questions-form-step-1)
step-2-form (fm/use-form
:initial {}
:validators [step-2-form-validator]
:spec ::questions-form-step-2)
step-3-form (fm/use-form
:initial {}
:validators [step-3-form-validator]
@ -260,6 +470,11 @@
:validators [step-4-form-validator]
:spec ::questions-form-step-4)
step-5-form (fm/use-form
:initial {}
:validators [step-5-form-validator]
:spec ::questions-form-step-5)
on-next
(mf/use-fn
(fn [form]
@ -275,27 +490,22 @@
(mf/use-fn
(mf/deps @clean-data)
(fn [form]
(let [questionnaire (merge @clean-data (:clean-data @form))]
(reset! clean-data questionnaire)
(st/emit! (du/mark-questions-as-answered questionnaire))
(let [data (merge @clean-data (:clean-data @form))]
(reset! clean-data data)
(st/emit! (du/mark-questions-as-answered data)))))
(cond
(contains? cf/flags :onboarding-newsletter)
(modal/show! {:type :onboarding-newsletter})
onboarding-a-b-test?
(cf/external-feature-flag "signup-background" "test")]
(contains? cf/flags :onboarding-team)
(modal/show! {:type :onboarding-team})
:else
(modal/hide!)))))
onboarding-a-b-test? (cf/external-feature-flag "signup-background" "test")]
[:div {:class (stl/css-case :modal-overlay true
:onboarding-a-b-test onboarding-a-b-test?)}
[:div {:class (stl/css-case
:modal-overlay true
:onboarding-a-b-test onboarding-a-b-test?)}
[:div {:class (stl/css :modal-container)
:ref container}
(case @step
1 [:& step-1 {:on-next on-next :on-prev on-prev :form step-1-form}]
2 [:& step-2 {:on-next on-next :on-prev on-prev :form step-2-form}]
3 [:& step-3 {:on-next on-next :on-prev on-prev :form step-3-form}]
4 [:& step-4 {:on-next on-submit :on-prev on-prev :form step-4-form}])]]))
4 [:& step-4 {:on-next on-next :on-prev on-prev :form step-4-form}]
5 [:& step-5 {:on-next on-submit :on-prev on-prev :form step-5-form}])]]))

View file

@ -11,9 +11,9 @@
}
.modal-container {
max-width: $s-744;
max-width: $s-960;
max-height: fit-content;
width: $s-744;
width: fit-content;
padding-inline: $s-100;
padding-block-start: $s-40;
padding-block-end: $s-72;
@ -47,15 +47,15 @@
@extend .modal-cancel-btn;
}
.radio-btns label,
.select-class span {
@include bodyMediumTypography;
}
// STEP 1
// .step-1 {
// max-height: $s-468;
// height: $s-468;
// }
.header-image {
height: $s-112;
height: $s-60;
width: auto;
margin-inline-start: auto;
}
@ -81,9 +81,15 @@
}
// STEP-2
.step-1,
.step-2,
.step-3,
.step-5 {
max-width: $s-540;
width: $s-540;
}
.step-2 {
grid-template-rows: $s-20 auto auto auto auto $s-32;
grid-template-rows: $s-20 auto auto $s-32;
}
.modal-question {
@ -103,36 +109,36 @@
.radio-wrapper {
display: grid;
grid-template-columns: 1fr;
gap: $s-8;
gap: $s-16;
}
// STEP-3
.step-3 {
grid-template-rows: $s-20 auto auto $s-32;
grid-template-rows: $s-20 auto auto auto auto $s-32;
}
.image-radio {
display: grid;
grid-template-rows: 1fr 1fr $s-32;
grid-template-columns: $s-88 $s-92 $s-92 $s-92 $s-88;
grid-template-areas:
". image1 image2 image3 ."
". image4 image5 image6 ."
"other other other other other";
grid-template-rows: 1fr 1fr;
grid-template-columns: $s-92 $s-92 $s-92;
row-gap: $s-16;
column-gap: $s-24;
justify-content: center;
}
.input-spacing {
height: $s-32;
width: calc(100% - $s-24);
margin-inline-start: $s-24;
width: 100%;
margin-block-end: $s-8;
}
.input-spacing input {
@include bodyMediumTypography;
}
// STEP-4
.step-4 {
grid-template-rows: $s-20 auto auto auto $s-32;
grid-template-rows: $s-20 auto auto $s-32;
row-gap: $s-16;
}

View file

@ -7,34 +7,29 @@
(ns app.main.ui.onboarding.team-choice
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dmc]
[app.common.data.macros :as dm]
[app.common.spec :as us]
[app.config :as cf]
[app.main.data.dashboard :as dd]
[app.main.data.events :as ev]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.refs :as refs]
[app.main.data.users :as du]
[app.main.store :as st]
[app.main.ui.components.forms :as fm]
[app.main.ui.icons :as i]
[app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[app.util.timers :as tm]
[cljs.spec.alpha :as s]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
(s/def ::name ::us/not-empty-string)
(s/def ::team-form
(s/keys :req-un [::name]))
(mf/defc team-modal-left
(mf/defc left-sidebar
{::mf/props :obj
::mf/private true}
[]
[:div {:class (stl/css :modal-left)}
[:h1 {:class (stl/css :modal-title)}
(tr "onboarding-v2.welcome.title")]
[:h2 {:class (stl/css :modal-subtitle)}
(tr "onboarding.team-modal.team-definition")]
[:p {:class (stl/css :modal-text)}
@ -61,99 +56,27 @@
[:p {:class (stl/css :modal-desc)}
(tr "onboarding.team-modal.create-team-feature-5")]]]])
(mf/defc onboarding-team-modal
{::mf/register modal/components
::mf/register-as :onboarding-team}
[]
(let [form (fm/use-form :spec ::team-form
:initial {}
:validators [(fm/validate-not-empty :name (tr "auth.name.not-all-space"))
(fm/validate-length :name fm/max-length-allowed (tr "auth.name.too-long"))])
on-submit
(mf/use-fn
(fn [form _]
(let [tname (get-in @form [:clean-data :name])]
(st/emit! (modal/show {:type :onboarding-team-invitations :name tname})
(ptk/event ::ev/event {::ev/name "choose-team-name"
::ev/origin "onboarding"
:name tname
:step 1})))))
on-skip
(fn []
(tm/schedule 400 #(st/emit! (modal/hide)
(ptk/event ::ev/event {::ev/name "create-team-later"
::ev/origin "onboarding"
:step 1}))))
teams (mf/deref refs/teams)
onboarding-a-b-test? (cf/external-feature-flag "signup-background" "test")]
(mf/with-effect [teams]
(when (> (count teams) 1)
(st/emit! (modal/hide))))
(when (< (count teams) 2)
[:div {:class (stl/css-case :modal-overlay true
:onboarding-a-b-test onboarding-a-b-test?)}
[:div.animated.fadeIn {:class (stl/css :modal-container)}
[:& team-modal-left]
[:div {:class (stl/css :separator)}]
[:div {:class (stl/css :modal-right)}
[:div {:class (stl/css :first-block)}
[:h2 {:class (stl/css :modal-subtitle)}
(tr "onboarding.team-modal.create-team")]
[:p {:class (stl/css :modal-text)}
(tr "onboarding.choice.team-up.create-team-desc")]
[:& fm/form {:form form
:class (stl/css :modal-form)
:on-submit on-submit}
[:& fm/input {:type "text"
:class (stl/css :team-name-input)
:name :name
:placeholder "Team name"
:label (tr "onboarding.choice.team-up.create-team-placeholder")}]
[:div {:class (stl/css :action-buttons)}
[:> fm/submit-button*
{:class (stl/css :accept-button)
:label (tr "onboarding.choice.team-up.continue-creating-team")}]]]]
[:div {:class (stl/css :second-block)}
[:h2 {:class (stl/css :modal-subtitle)}
(tr "onboarding.choice.team-up.start-without-a-team")]
[:p {:class (stl/css :modal-text)}
(tr "onboarding.choice.team-up.start-without-a-team-description")]
[:div {:class (stl/css :action-buttons)}
[:button {:class (stl/css :accept-button)
:on-click on-skip}
(tr "onboarding.choice.team-up.continue-without-a-team")]]]]
[:div {:class (stl/css :paginator)} "1/2"]]])))
(defn get-available-roles
[]
[{:value "editor" :label (tr "labels.editor")}
{:value "admin" :label (tr "labels.admin")}])
(s/def ::emails (s/and ::us/set-of-valid-emails))
(s/def ::role ::us/keyword)
(s/def ::invite-form
(s/keys :req-un [::role ::emails]))
;; This is the final step of team creation, consists in provide a
;; shortcut for invite users.
(defn- get-available-roles
[]
[{:value "editor" :label (tr "labels.editor")}
{:value "admin" :label (tr "labels.admin")}])
(mf/defc team-form-step-2
{::mf/props :obj}
[{:keys [name on-back]}]
(let [initial (mf/use-memo
#(do {:role "editor"
:name name}))
(mf/defc onboarding-team-invitations-modal
{::mf/register modal/components
::mf/register-as :onboarding-team-invitations
::mf/props :obj}
[{:keys [name]}]
(let [initial (mf/use-memo (constantly
{:role "editor"
:name name}))
form (fm/use-form :spec ::invite-form
:initial initial)
params (:clean-data @form)
emails (:emails params)
@ -161,51 +84,48 @@
on-success
(mf/use-fn
(fn [_form response]
(let [team-id (:id response)]
(st/emit!
(modal/hide)
(rt/nav :dashboard-projects {:team-id team-id}))
(tm/schedule 400 #(st/emit!
(modal/hide))))))
(fn [response]
(let [team-id (:id response)]
(st/emit! (du/update-profile-props {:onboarding-team-id team-id
:onboarding-viewed true})
(rt/nav :dashboard-projects {:team-id team-id})))))
on-error
(mf/use-fn
(fn [_form _cause]
(st/emit! (msg/error "Error on creating team."))))
(fn [_]
(st/emit! (msg/error (tr "errors.generic")))))
;; The SKIP branch only creates the team, without invitations
on-invite-later
(mf/use-fn
(fn [_]
(let [mdata {:on-success (partial on-success form)
:on-error (partial on-error form)}
(fn [{:keys [name]}]
(let [mdata {:on-success on-success
:on-error on-error}
params {:name name}]
(st/emit! (dd/create-team (with-meta params mdata))
(ptk/event ::ev/event {::ev/name "create-team-and-invite-later"
::ev/origin "onboarding"
:name name
:step 2})))))
(ptk/data-event ::ev/event
{::ev/name "onboarding-step"
:label "team:create-team-and-invite-later"
:team-name name
:step 7})
(ptk/data-event ::ev/event
{::ev/name "onboarding-finish"})))))
;; The SUBMIT branch creates the team with the invitations
on-invite-now
(mf/use-fn
(fn [form]
(let [mdata {:on-success (partial on-success form)
:on-error (partial on-error form)}
params (:clean-data @form)
emails (:emails params)]
(fn [{:keys [name] :as params}]
(let [mdata {:on-success on-success
:on-error on-error}]
(st/emit! (if (> (count emails) 0)
;; If the user is only inviting to itself we don't call to create-team-with-invitations
(dd/create-team-with-invitations (with-meta params mdata))
(dd/create-team (with-meta {:name name} mdata)))
(ptk/event ::ev/event {::ev/name "create-team-and-send-invitations"
::ev/origin "onboarding"
:invites (count emails)
:role (:role params)
:name name
:step 2})))))
(st/emit! (dd/create-team-with-invitations (with-meta params mdata))
(ptk/data-event ::ev/event
{::ev/name "onboarding-step"
:label "team:create-team-and-invite"
:invites (count emails)
:team-name name
:role (:role params)
:step 7})
(ptk/data-event ::ev/event
{::ev/name "onboarding-finish"})))))
on-submit
(mf/use-fn
@ -213,55 +133,146 @@
(let [params (:clean-data @form)
emails (:emails params)]
(if (> (count emails) 0)
(on-invite-now form)
(on-invite-later form))
(modal/hide!))))
onboarding-a-b-test? (cf/external-feature-flag "signup-background" "test")]
(on-invite-now params)
(on-invite-later params)))))]
[:div {:class (stl/css-case :modal-overlay true
:onboarding-a-b-test onboarding-a-b-test?)}
[:div.animated.fadeIn {:class (stl/css :modal-container)}
[:& team-modal-left]
[:*
[:div {:class (stl/css :modal-right-invitations)}
[:h2 {:class (stl/css :modal-subtitle)} (tr "onboarding.choice.team-up.invite-members")]
[:p {:class (stl/css :modal-text)} (tr "onboarding.choice.team-up.invite-members-info")]
[:& fm/form {:form form
:class (stl/css :modal-form-invitations)
:on-submit on-submit}
[:div {:class (stl/css :role-select)}
[:p {:class (stl/css :role-title)} (tr "onboarding.choice.team-up.roles")]
[:& fm/select {:name :role :options roles}]]
[:div {:class (stl/css :separator)}]
[:div {:class (stl/css :modal-right-invitations)}
[:h2 {:class (stl/css :modal-subtitle)} (tr "onboarding.choice.team-up.invite-members")]
[:p {:class (stl/css :modal-text)} (tr "onboarding.choice.team-up.invite-members-info")]
[:div {:class (stl/css :invitation-row)}
[:& fm/multi-input {:type "email"
:name :emails
:auto-focus? true
:trim true
:valid-item-fn us/parse-email
:caution-item-fn #{}
:label (tr "modals.invite-member.emails")
:on-submit on-submit}]]
[:div {:class (stl/css :action-buttons)}
[:button {:class (stl/css :back-button)
:on-click on-back}
(tr "labels.back")]
[:> fm/submit-button*
{:class (stl/css :accept-button)
:label (if (> (count emails) 0)
(tr "onboarding.choice.team-up.create-team-and-invite")
(tr "onboarding.choice.team-up.create-team-without-invite"))}]]
[:div {:class (stl/css :modal-hint)}
"(" (tr "onboarding.choice.team-up.create-team-and-send-invites-description") ")"]]]
[:div {:class (stl/css :paginator)} "2/2"]]))
(mf/defc team-form-step-1
{::mf/props :obj
::mf/private true}
[{:keys [on-submit]}]
(let [validators (mf/with-memo []
[(fm/validate-not-empty :name (tr "auth.name.not-all-space"))
(fm/validate-length :name fm/max-length-allowed (tr "auth.name.too-long"))])
form (fm/use-form
:spec ::team-form
:initial {}
:validators validators)
on-submit*
(mf/use-fn
(fn [form]
(let [name (dm/get-in @form [:clean-data :name])]
(st/emit! (ptk/data-event ::ev/event
{::ev/name "onboarding-step"
:label "team:choice-team-name"
:step 7}))
(on-submit name))))
on-skip
(mf/use-fn
(fn []
(st/emit! (du/update-profile-props {:onboarding-viewed true})
(ptk/data-event ::ev/event
{::ev/name "onboarding-step"
:label "team:skip-team-creation"
:step 7})
(ptk/data-event ::ev/event
{::ev/name "onboarding-finish"}))))]
[:*
[:div {:class (stl/css :modal-right)}
[:div {:class (stl/css :first-block)}
[:h2 {:class (stl/css :modal-subtitle)}
(tr "onboarding.team-modal.create-team")]
[:p {:class (stl/css :modal-text)}
(tr "onboarding.choice.team-up.create-team-desc")]
[:& fm/form {:form form
:class (stl/css :modal-form-invitations)
:on-submit on-submit}
[:div {:class (stl/css :role-select)}
[:p {:class (stl/css :role-title)} (tr "onboarding.choice.team-up.roles")]
[:& fm/select {:name :role :options roles}]]
:class (stl/css :modal-form)
:on-submit on-submit*}
[:div {:class (stl/css :invitation-row)}
[:& fm/multi-input {:type "email"
:name :emails
:auto-focus? true
:trim true
:valid-item-fn us/parse-email
:caution-item-fn #{}
:label (tr "modals.invite-member.emails")
:on-submit on-submit}]]
[:& fm/input {:type "text"
:class (stl/css :team-name-input)
:name :name
:placeholder "Team name"
:label (tr "onboarding.choice.team-up.create-team-placeholder")}]
[:div {:class (stl/css :action-buttons)}
[:button {:class (stl/css :back-button)
:on-click #(st/emit! (modal/show {:type :onboarding-team})
(ptk/event ::ev/event {::ev/name "invite-members-back"
::ev/origin "onboarding"
:name name
:step 2}))}
(tr "labels.back")]
[:> fm/submit-button*
{:class (stl/css :accept-button)
:label (if (> (count emails) 0)
(tr "onboarding.choice.team-up.create-team-and-invite")
(tr "onboarding.choice.team-up.create-team-without-invite"))}]]
[:div {:class (stl/css :modal-hint)}
(dmc/str "(" (tr "onboarding.choice.team-up.create-team-and-send-invites-description") ")")]]]
:label (tr "onboarding.choice.team-up.continue-creating-team")}]]]]
[:div {:class (stl/css :second-block)}
[:h2 {:class (stl/css :modal-subtitle)}
(tr "onboarding.choice.team-up.start-without-a-team")]
[:p {:class (stl/css :modal-text)}
(tr "onboarding.choice.team-up.start-without-a-team-description")]
[:div {:class (stl/css :action-buttons)}
[:button {:class (stl/css :accept-button)
:on-click on-skip}
(tr "onboarding.choice.team-up.continue-without-a-team")]]]]
[:div {:class (stl/css :paginator)} "1/2"]]))
(s/def ::name ::us/not-empty-string)
(s/def ::team-form
(s/keys :req-un [::name]))
(mf/defc onboarding-team-modal
{::mf/props :obj}
[]
(let [name* (mf/use-state nil)
name (deref name*)
on-submit
(mf/use-fn
(fn [tname]
(swap! name* (constantly tname))))
[:div {:class (stl/css :paginator)} "2/2"]]]))
on-back
(mf/use-fn
(fn []
(swap! name* (constantly nil))))
onboarding-a-b-test?
(cf/external-feature-flag "signup-background" "test")]
[:div {:class (stl/css-case
:modal-overlay true
:onboarding-a-b-test onboarding-a-b-test?)}
[:div.animated.fadeIn {:class (stl/css :modal-container)}
[:& left-sidebar]
[:div {:class (stl/css :separator)}]
(if name
[:& team-form-step-2 {:name name :on-back on-back}]
[:& team-form-step-1 {:on-submit on-submit}])]]))

View file

@ -160,6 +160,7 @@
.custom-input-token {
@extend .input-element;
@include bodySmallTypography;
margin: 0;
flex-grow: 1;
&:focus {

View file

@ -134,6 +134,8 @@
page-id (:id page)
file-id (:id file)
frame-id (:id frame)
vsize (-> (mf/deref refs/viewer-local)
:viewport-size)
tpos-ref (mf/with-memo [page-id]
(-> (l/in [:pages page-id :options :comment-threads-position])
@ -216,6 +218,7 @@
[:& cmt/thread-comments
{:thread thread
:position-modifier modifier1
:viewport {:offset-x 0 :offset-y 0 :width (:width vsize) :height (:height vsize)}
:users users
:zoom zoom}])

View file

@ -92,6 +92,7 @@
.suffix-input {
@extend .input-element;
@include bodySmallTypography;
grid-column: span 3;
}

View file

@ -63,6 +63,7 @@
.input-text {
@extend .input-element;
@include bodySmallTypography;
color: var(--input-foreground-color-active);
padding-left: $s-8;
margin: 0;

View file

@ -17,6 +17,7 @@
@include flexRow;
.input-wrapper {
@extend .input-element;
@include bodySmallTypography;
width: $s-84;
display: flex;
align-items: baseline;
@ -26,6 +27,7 @@
@include flexRow;
.input-wrapper {
@extend .input-element;
@include bodySmallTypography;
width: $s-84;
&.hex {
width: $s-172;

View file

@ -36,6 +36,7 @@
.input-wrapper {
@extend .input-with-label;
@include bodySmallTypography;
label {
text-transform: none;
}

View file

@ -49,6 +49,7 @@
}
.input-wrapper {
@extend .input-with-label;
@include bodySmallTypography;
margin-bottom: $s-8;
}
.action-buttons {

View file

@ -59,6 +59,7 @@
:ref dref
:on-click on-select-shape
:on-context-menu on-context-menu
:data-testid "layer-row"
:class (stl/css-case
:layer-row true
:highlight highlighted?

View file

@ -100,6 +100,7 @@
.second-row {
@extend .input-element;
@include bodySmallTypography;
width: $s-92;
.label {
padding-left: $s-8;

View file

@ -92,6 +92,7 @@
.suffix-input {
grid-column: span 3;
@extend .input-element;
@include bodySmallTypography;
}
.export-btn {

View file

@ -77,6 +77,7 @@
border-radius: 0 $br-8 $br-8 0;
.numeric-input {
@extend .input-base;
@include bodySmallTypography;
}
}
.editable-select-wrapper {
@ -93,6 +94,7 @@
border: $s-1 solid var(--input-border-color);
.numeric-input {
@extend .input-base;
@include bodySmallTypography;
margin: 0;
padding: 0;
}
@ -196,6 +198,7 @@
}
.height {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
.icon-text {
padding-top: $s-1;
@ -204,6 +207,7 @@
.gutter,
.margin {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
.icon {
&.rotated svg {

View file

@ -33,6 +33,7 @@
.area-input {
@extend .input-element;
@include bodySmallTypography;
width: 100%;
padding: $s-8;
}
@ -51,6 +52,7 @@
.coord-input {
@extend .input-element;
@include bodySmallTypography;
border-radius: 0 $br-8 $br-8 0;
border-left: $s-1 solid var(--panel-background-color);
}

View file

@ -143,6 +143,7 @@
}
.input-element-wrapper {
@extend .input-element;
@include bodySmallTypography;
grid-area: content;
}
.buttons-wrapper {
@ -319,6 +320,7 @@
.flow-input {
@extend .input-base;
@include bodySmallTypography;
background-color: transparent;
height: $s-28;
}

View file

@ -19,6 +19,7 @@
}
.input {
@extend .input-element;
@include bodySmallTypography;
width: $s-60;
}
.actions {

View file

@ -84,6 +84,7 @@
gap: $s-4;
.column-gap {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
&.disabled {
@extend .disabled-input;
@ -91,6 +92,7 @@
}
.row-gap {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
&.disabled {
@extend .disabled-input;
@ -113,6 +115,7 @@
.padding-simple {
@extend .input-element;
@include bodySmallTypography;
max-width: $s-108;
}
}
@ -124,6 +127,7 @@
.padding-multiple {
@extend .input-element;
@include bodySmallTypography;
max-width: $s-108;
}
}
@ -239,6 +243,7 @@
.track-info-value {
@extend .input-element;
@include bodySmallTypography;
border-radius: 0;
border-right: $s-1 solid var(--panel-background-color);
}

View file

@ -58,6 +58,7 @@
.z-index-wrapper {
@extend .input-element;
@include bodySmallTypography;
width: $s-60;
}
@ -94,6 +95,7 @@
.vertical-margin,
.horizontal-margin {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
}
}
@ -109,6 +111,7 @@
.left-margin,
.right-margin {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
}
@ -127,6 +130,7 @@
.layout-item-max-w,
.layout-item-max-h {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
.icon-text {
justify-content: flex-start;

View file

@ -9,6 +9,7 @@
(:require
[app.common.data :as d]
[app.common.geom.shapes :as gsh]
[app.common.logic.shapes :as cls]
[app.common.types.shape.layout :as ctl]
[app.common.types.shape.radius :as ctsr]
[app.main.constants :refer [size-presets]]
@ -325,16 +326,15 @@
(fn [event]
(let [value (-> event dom/get-target dom/checked?)
undo-id (js/Symbol)]
(do
(st/emit! (dwu/start-undo-transaction undo-id)
(dwsh/update-shapes ids (fn [shape] (assoc shape :hide-in-viewer (not value)))))
(st/emit! (dwu/start-undo-transaction undo-id)
(dwsh/update-shapes ids (fn [shape] (cls/change-show-in-viewer shape (not value)))))
(when-not value
(when-not value
;; when a frame is no longer shown in view mode, cannot have
;; interactions that navigate to it.
(apply st/emit! (map #(dwi/remove-all-interactions-nav-to %) ids)))
(apply st/emit! (map #(dwi/remove-all-interactions-nav-to %) ids)))
(st/emit! (dwu/commit-undo-transaction undo-id))))))]
(st/emit! (dwu/commit-undo-transaction undo-id)))))]
(mf/use-layout-effect
(mf/deps radius-mode @radius-multi?)

View file

@ -114,6 +114,7 @@
.height,
.width {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
.icon-text {
padding-top: $s-1;
@ -145,6 +146,7 @@
.x-position,
.y-position {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
.icon-text {
padding-top: $s-1;
@ -163,6 +165,7 @@
.rotation {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
.icon-text {
padding-top: $s-1;
@ -181,6 +184,7 @@
.radius-1 {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
}
@ -190,6 +194,7 @@
gap: $s-4;
.small-input {
@extend .input-element;
@include bodySmallTypography;
width: $s-52;
}
}

View file

@ -114,6 +114,7 @@
.spread-input,
.offset-y-input {
@extend .input-element;
@include bodySmallTypography;
width: $s-60;
min-width: $s-60;
align-items: baseline;

View file

@ -37,6 +37,7 @@
.attr-input {
@extend .input-element;
@include bodySmallTypography;
width: $s-124;
}

View file

@ -308,6 +308,7 @@
.line-height,
.letter-spacing {
@extend .input-element;
@include bodySmallTypography;
.icon {
@include flexCenter;
width: $s-28;
@ -339,6 +340,7 @@
padding: $s-8;
.numeric-input {
@extend .input-base;
@include bodySmallTypography;
padding: 0;
}
}

View file

@ -41,6 +41,7 @@
.color-name-wrapper {
@extend .input-element;
@include bodySmallTypography;
flex-grow: 1;
width: 100%;
min-width: 0;
@ -166,6 +167,7 @@
.opacity-element-wrapper {
@extend .input-element;
@include bodySmallTypography;
width: $s-60;
border-radius: 0 $br-8 $br-8 0;
.opacity-input {

View file

@ -12,6 +12,7 @@
@include flexRow;
.stroke-width-input-element {
@extend .input-element;
@include bodySmallTypography;
width: $s-60;
}
.select-wrapper {

View file

@ -7,6 +7,7 @@
(ns app.util.forms
(:refer-clojure :exclude [uuid])
(:require
[app.common.data :as d]
[app.common.spec :as us]
[app.util.i18n :refer [tr]]
[cljs.spec.alpha :as s]
@ -69,11 +70,15 @@
(::s/problems (s/explain-data spec (:data state))))
errors (reduce interpret-problem {} problems)
errors (reduce (fn [errors vf]
(merge errors (vf errors (:data state))))
errors
validators)
errors (merge errors (:errors state))]
errors (merge (:errors state) errors)
errors (d/without-nils errors)]
(assoc state
:errors errors

View file

@ -278,7 +278,6 @@
old-id (parser/get-id node)
interactions (->> (parser/parse-interactions node)
(mapv #(update % :destination resolve)))
data (-> (parser/parse-data type node)
(resolve-data-ids type context)
(cond-> (some? old-id)

View file

@ -244,19 +244,22 @@
(first))
;; The nodes with the "frame-background" class can have some anidation depending on the strokes they have
g-nodes (find-all-nodes node :g)
defs-nodes (flatten (map #(find-all-nodes % :defs) g-nodes))
gg-nodes (flatten (map #(find-all-nodes % :g) g-nodes))
g-nodes (find-all-nodes node :g)
defs-nodes (flatten (map #(find-all-nodes % :defs) g-nodes))
gg-nodes (flatten (map #(find-all-nodes % :g) g-nodes))
;; The first g node contains the opacity for frames
main-g-node (first g-nodes)
rect-nodes (flatten [[(find-all-nodes node :rect)]
(map #(find-all-nodes % #{:rect :path}) defs-nodes)
(map #(find-all-nodes % #{:rect :path}) g-nodes)
(map #(find-all-nodes % #{:rect :path}) gg-nodes)])
svg-node (d/seek #(= "frame-background" (get-in % [:attrs :class])) rect-nodes)]
rect-nodes (flatten [[(find-all-nodes node :rect)]
(map #(find-all-nodes % #{:rect :path}) defs-nodes)
(map #(find-all-nodes % #{:rect :path}) g-nodes)
(map #(find-all-nodes % #{:rect :path}) gg-nodes)])
svg-node (d/seek #(= "frame-background" (get-in % [:attrs :class])) rect-nodes)]
(merge
(add-attrs {} (:attrs frame-clip-rect-node))
(add-attrs {} (:attrs svg-node))
(add-attrs {} (:attrs main-g-node))
node-attrs))
(= type :svg-raw)

View file

@ -36,11 +36,6 @@ msgstr "ترغب في التجربة فحسب؟"
msgid "auth.demo-warning"
msgstr "هذه خدمة تجريبية ، لا تستخدمها للعمل الحقيقي ، سيتم مسح المشاريع بشكل دوري."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "البريد الالكتروني"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "هل نسيت كلمة السر؟"
@ -136,10 +131,6 @@ msgstr "لا تملك حساب بعد؟"
msgid "auth.register-submit"
msgstr "إنشاء حساب"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "إنه مجاني ، مفتوح المصدر"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "إنشاء حساب"

View file

@ -35,10 +35,6 @@ msgstr ""
"এটি একটি ডেমো সার্ভিস। প্রয়োজনীয় কোনো কাজে ব্যবহার করবেন না। কিছু সময় পর "
"প্রজেক্টগুলো মুছে ফেলা হবে।"
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "ইমেইল"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "পাসওয়ার্ড ভুলে গেছেন?"

View file

@ -39,11 +39,6 @@ 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/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Correu electrònic"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Heu oblidat la contrasenya?"
@ -139,10 +134,6 @@ msgstr "No teniu un compte?"
msgid "auth.register-submit"
msgstr "Creeu un compte"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "És gratuït, és de codi obert"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Crea un compte"
@ -154,12 +145,6 @@ msgstr "La solució de codi obert per a dissenyar i prototipar."
msgid "auth.terms-of-service"
msgstr "Condicions del servei"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"En crear un compte nou, accepteu les condicions del servei i la política de "
"privadesa."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "S'ha enviat un correu de verificació a"

View file

@ -39,11 +39,6 @@ msgstr ""
"Toto je DEMO služba, NEPOUŽÍVEJTE ji pro skutečnou práci, projekty budou "
"pravidelně mazány."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Email"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Zapomněli jste heslo?"
@ -151,10 +146,6 @@ msgstr "Ještě nemáte účet?"
msgid "auth.register-submit"
msgstr "Vytvořit účet"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Je to zdarma, je to open source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Vytvořit účet"
@ -5120,4 +5111,4 @@ msgid "workspace.updates.update"
msgstr "Aktualizace"
msgid "workspace.viewport.click-to-close-path"
msgstr "Kliknutím zavřete cestu"
msgstr "Kliknutím zavřete cestu"

View file

@ -39,10 +39,6 @@ msgstr ""
"Det her er en DEMO service, BRUG IKKE for rigtigt arbejde, projekterne vil "
"blive slettet periodevis."
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Email"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Glemt adgangskode?"
@ -135,10 +131,6 @@ msgstr "Ingen konto?"
msgid "auth.register-submit"
msgstr "Opret en konto"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Det er gratis, det er Open Source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Opret en konto"
@ -147,12 +139,6 @@ msgstr "Opret en konto"
msgid "auth.sidebar-tagline"
msgstr "Open-source løsningen for design og prototyping."
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"Når du opretter en ny konto, accepterer du vores servicevilkår og "
"fortrolighedspolitik."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Vi har sendt en bekræftelsesmail til"

View file

@ -39,11 +39,6 @@ msgstr ""
"Das ist eine DEMO-VERSION, verwenden Sie es NICHT zum Arbeiten, die Projekte "
"werden regelmäßig gelöscht."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "E-Mail"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Passwort vergessen?"
@ -176,10 +171,6 @@ msgstr "Noch kein Konto?"
msgid "auth.register-submit"
msgstr "Konto erstellen"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Kostenlos, es ist Open Source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Konto erstellen"

View file

@ -39,11 +39,6 @@ msgstr ""
"Αυτή είναι μια υπηρεσία DEMO, ΜΗ ΧΡΗΣΙΜΟΠΟΙΕΙΤΕ για πραγματική εργασία, τα "
"έργα θα σβήνονται περιοδικά."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Email"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Ξεχάσατε τον κωδικό;"
@ -134,10 +129,6 @@ msgstr "Δεν έχετε λογαριασμό;"
msgid "auth.register-submit"
msgstr "Δημιουργία λογαριασμού"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Είναι δωρεάν, είναι Open Source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Δημιουργία λογαριασμού"
@ -146,12 +137,6 @@ msgstr "Δημιουργία λογαριασμού"
msgid "auth.sidebar-tagline"
msgstr "Η λύση ανοιχτού κώδικα για σχεδιασμό και πρωτότυπο."
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"Δημιουργώντας έναν νέο λογαριασμό, αποδέχεστε τους όρους παροχής υπηρεσιών "
"και την πολιτική απορρήτου."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Εχουμε στείλει ενα mail επαλήθευσης "

View file

@ -38,8 +38,8 @@ msgstr ""
"periodically wiped."
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Email"
msgid "auth.work-email"
msgstr "Work email"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
@ -55,7 +55,11 @@ msgstr "Log into my account"
#: src/app/main/ui/auth/register.cljs
msgid "auth.login-here"
msgstr "Login here"
msgstr "Login here."
#: src/app/main/ui/auth/register.cljs
msgid "auth.check-mail"
msgstr "Check your email"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-submit"
@ -65,6 +69,14 @@ msgstr "Login"
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
msgid "auth.register-tagline"
msgstr "With a free Penpot account, you can create unlimited teams and collaborate with other designers and developers on as many projects as you like. "
#: src/app/main/ui/auth/login.cljs
msgid "auth.register-account-tagline"
msgstr "Let us know what we should call you on the dashboard and in emails."
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-github-submit"
msgstr "GitHub"
@ -156,14 +168,14 @@ msgstr "No account yet?"
msgid "auth.register-submit"
msgstr "Create an account"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "It's free, it's Open Source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Create an account"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-account-title"
msgstr "Your name"
#: src/app/main/ui/auth.cljs
msgid "auth.sidebar-tagline"
msgstr "The open-source solution for design and prototyping."
@ -178,10 +190,9 @@ msgstr ""
#: src/app/main/ui/auth/register.cljs
#, markdown
msgid "auth.terms-privacy-agreement-md"
msgid "auth.terms-and-privacy-agreement"
msgstr ""
"When creating a new account, you agree to our [terms of service](%s) and "
"[privacy policy](%s)."
"I agree to the [terms of service](%s) and [privacy policy](%s)."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
@ -2382,24 +2393,52 @@ msgid "profile.recovery.go-to-login"
msgstr "Go to login"
#: src/app/main/ui/onboarding/questions.cljs
msgid "question.design-tool-more-experienced-with"
msgstr "Which is the design tool you have more experience with?"
msgid "questions.step1-title"
msgstr "Help us get to know you"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.11-30"
msgstr "11-30"
msgid "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
msgid "questions.2-10"
msgstr "2-10"
msgid "questions.step1-question1"
msgstr "What will you be using Penpot for?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.31-50"
msgstr "31-50"
msgid "questions.use-work"
msgstr "Work"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.a-lot"
msgstr "A lot"
msgid "questions.use-education"
msgstr "Education"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.use-personal"
msgstr "Personal"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.step1-question2"
msgstr "What brings you to Penpot today?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.reasons.exploring"
msgstr "Just exploring"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.reasons.fit"
msgstr "Reviewing whether Penpot is a good fit for my team"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.reasons.alternative"
msgstr "Looking for an alternative to Figma, XD, etc"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.reasons.testing"
msgstr "Testing before self-hosting"
#: src/app/main/ui/onboarding/questions.cljs
msgid "question.design-tool-more-used"
msgstr "Which one of these tools do you use the most?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.adobe-xd"
@ -2409,148 +2448,182 @@ msgstr "Adobe XD"
msgid "questions.canva"
msgstr "Canva"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.describe-your-experience-working-on"
msgstr "How would you best describe your experience working on..."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.designer"
msgstr "Designer"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.developer"
msgstr "Developer"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.discover-more-about-penpot"
msgstr "Discover more about Penpot"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.figma"
msgstr "Figma"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.founder"
msgstr "Founder/VP"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.freelancer"
msgstr "I'm a freelancer"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.get-the-code-from-my-team-project"
msgstr "Get the code from my team project "
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.interface-design-visual-assets-design-systems"
msgstr "... interface design, visual assets, design systems, etc."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.invision"
msgstr "InVision"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.leave-feedback-for-my-team-project"
msgstr "Leave feedback for my team project"
msgid "questions.sketch"
msgstr "Sketch"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.lets-get-started"
msgstr "Let's get started!"
msgid "questions.step3-title"
msgstr "Tell us about your job"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.manager"
msgstr "Product or Project manager"
msgid "questions.step3.question1"
msgstr "What kind of work do you do?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.marketing"
msgid "questions.work-type.ux"
msgstr "Product or UX design"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-type.dev"
msgstr "Development"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-type.student"
msgstr "student"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-type.graphic"
msgstr "Graphic design"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-type.marketing"
msgstr "Marketing"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-type.product"
msgstr "Product Managment"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.step3.question2"
msgstr "What's your role?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role.team-leader"
msgstr "Team Leader"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role.team-member"
msgstr "Team member"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role.freelancer"
msgstr "Freelancer"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role.founder"
msgstr "CEO or Founder"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role.director"
msgstr "Director"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.company-size"
msgstr "What's the size of your company?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.more-than-50"
msgstr "More than 50"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.never-used-one"
msgstr "None"
msgid "questions.31-50"
msgstr "31-50"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.next"
msgstr "Next"
msgid "questions.11-30"
msgstr "11-30"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.none"
msgstr "None"
msgid "questions.2-10"
msgstr "2-10"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.other"
msgstr "Other (specify)"
msgid "questions.freelancer"
msgstr "I'm a freelancer"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.personal-project"
msgstr "Im working in a personal project"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.step4-title"
msgstr "Where would you like to get started?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.starting-ui"
msgstr "Design the UI/UX of an app"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.starting-wireframing"
msgstr "Wireframing"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.starting-prototyping"
msgstr "Prototyping"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.starting-ds"
msgstr "Creating Desing Systems"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.starting-code"
msgstr "Generating real code designs"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.step5-title"
msgstr "How did you hear about Penpot?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.referer.youtube"
msgstr "YouTube"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.referer.event"
msgstr "Event"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.referer.search"
msgstr "Search Engine (Google, Yahoo, Bing)"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.referer.social"
msgstr "Social Media (X, Linkedin, FB, etc)"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.referer.article"
msgstr "Article (Blog, Post, Newsletter)"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.lets-get-started"
msgstr "Let's get started!"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.next"
msgstr "Next"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.other"
msgstr "Other (specify)"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.other-short"
msgstr "Other"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.previous"
msgstr "Previous"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.questions-how-are-you-planning-to-use-penpot"
msgstr "How are you planning to use Penpot?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role"
msgstr "What's your role?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.select-option"
msgstr "Select option"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.sketch"
msgstr "Sketch"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.some"
msgstr "Some"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.start"
msgstr "Start"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.start-to-work-on-my-project"
msgstr "Start to work on my project"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.student-teacher"
msgstr "Student or teacher"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.team-size"
msgstr "What's the size of your team?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.test-penpot-to-see-if-its-a-fit-for-team"
msgstr "Test Penpot to see if it's a fit for team "
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.try-out-before-using-penpot-on-premise"
msgstr "Try out before using Penpot on-premise"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.wireframes-user-journeys-flows-navigation-trees"
msgstr "... wireframes, user journeys & flows, navigation trees, etc."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-in-concept-ideas"
msgstr "Work in concept ideas"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.your-feedback-will-help-us"
msgstr ""
"Your feedback will help us understand what your habits and preferences are "
"so that we can keep making Penpot such a useful and enjoyable tool."
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs,
msgid "settings.detach"
msgstr "Detach"
@ -2563,6 +2636,7 @@ msgstr "Mixed"
msgid "settings.select-this-color"
msgstr "Select items using this style"
# SECTIONS
msgid "shortcut-section.basics"
msgstr "Basics"

View file

@ -39,9 +39,8 @@ msgstr ""
"Este es un servicio de DEMOSTRACIÓN. NO USAR para trabajo real, los "
"proyectos serán borrados periodicamente."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.work-email"
msgstr "Correo electrónico"
#: src/app/main/ui/auth/login.cljs
@ -58,7 +57,7 @@ msgstr "Entrar en mi cuenta"
#: src/app/main/ui/auth/register.cljs
msgid "auth.login-here"
msgstr "Inicia sesión aquí"
msgstr "Inicia sesión aquí."
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-submit"
@ -70,6 +69,14 @@ 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
msgid "auth.register-tagline"
msgstr "Con una cuenta gratuita de Penpot, puedes crear un número ilimitado de equipos y colaborar con otros diseñadores y desarrolladores en tantos proyectos como quieras."
#: src/app/main/ui/auth/login.cljs
msgid "auth.register-account-tagline"
msgstr "Cuéntanos cómo quieres que nos dirijamos a tí."
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-github-submit"
msgstr "GitHub"
@ -163,14 +170,14 @@ msgstr "¿No tienes una cuenta?"
msgid "auth.register-submit"
msgstr "Crear una cuenta"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Es gratis, es Open Source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Crear una cuenta"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-account-title"
msgstr "Tu nombre"
#: src/app/main/ui/auth.cljs
msgid "auth.sidebar-tagline"
msgstr "La solución de código abierto para diseñar y prototipar."
@ -190,10 +197,20 @@ msgstr ""
"Al crear una nueva cuenta, aceptas nuestros [términos de servicio](%s) y "
"[política de privacidad](%s)."
#: src/app/main/ui/auth/register.cljs
#, markdown
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
msgid "auth.verification-email-sent"
msgstr "Hemos enviado un email de verificación a"
#: src/app/main/ui/auth/register.cljs
msgid "auth.check-mail"
msgstr "Comprueba tu correo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "branding-illustrations-marketing-pieces"
msgstr "diseño de marca, ilustraciones, piezas de marketing..."
@ -2455,25 +2472,84 @@ msgstr "Penpot"
msgid "profile.recovery.go-to-login"
msgstr "Ir al login"
#: src/app/main/ui/onboarding/questions.cljs
msgid "question.design-tool-more-experienced-with"
msgstr "¿Cuál es la herramienta de diseño con la que tienes más experiencia?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.11-30"
msgstr "11-30"
msgid "questions.step1-title"
msgstr "Ayúdanos a conocerte"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.2-10"
msgstr "2-10"
msgid "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
msgid "questions.31-50"
msgstr "31-50"
msgid "questions.step1-question1"
msgstr "¿Para qué utilizarás Penpot?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.a-lot"
msgstr "Mucha"
msgid "questions.use-work"
msgstr "Trabajo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.use-education"
msgstr "Educación"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.use-personal"
msgstr "Personal"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.step1-question2"
msgstr "¿Qué te ha traído a Penpot?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.reasons.exploring"
msgstr "Estoy echando un vistazo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.reasons.fit"
msgstr "Averiguar si Penpot es una buena opción para mi equipo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.reasons.alternative"
msgstr "Buscar una alternativa a Figma, XD, etc."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.reasons.testing"
msgstr "Probar antes de self-hosting"
#: src/app/main/ui/onboarding/questions.cljs
msgid "question.design-tool-more-used"
msgstr "¿Cuál de estas herramientas utilizas más?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.adobe-xd"
@ -2483,148 +2559,182 @@ msgstr "Adobe XD"
msgid "questions.canva"
msgstr "Canva"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.describe-your-experience-working-on"
msgstr "Cuánta experiencia dirías que tienes trabajando con..."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.designer"
msgstr "Diseño"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.developer"
msgstr "Desarrollo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.discover-more-about-penpot"
msgstr "Conocer Penpot mejor"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.figma"
msgstr "Figma"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.founder"
msgstr "Dirección"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.freelancer"
msgstr "Soy freelancer"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.get-the-code-from-my-team-project"
msgstr "Obtener código de un proyecto"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.interface-design-visual-assets-design-systems"
msgstr "diseño de interfaz, visual, sistemas de diseño..."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.invision"
msgstr "InVision"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.leave-feedback-for-my-team-project"
msgstr "Dejar comentarios en un proyecto"
msgid "questions.sketch"
msgstr "Sketch"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.lets-get-started"
msgstr "¡Empecemos!"
msgid "questions.step3-title"
msgstr "Háblanos de tu trabajo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.manager"
msgstr "Gestión de producto o proyecto"
msgid "questions.step3.question1"
msgstr "¿Qué tipo de trabajo haces?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.marketing"
msgid "questions.work-type.ux"
msgstr "Diseño de Producto o UX"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-type.dev"
msgstr "Desarrollo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-type.student"
msgstr "Estudiante"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-type.graphic"
msgstr "Diseño gráfico"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-type.marketing"
msgstr "Marketing"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-type.product"
msgstr "Product Managment"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.step3.question2"
msgstr "¿Cuál es tu puesto?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role.team-leader"
msgstr "Líder de equipo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role.team-member"
msgstr "Miembro de equipo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role.freelancer"
msgstr "Autónomo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role.founder"
msgstr "CEO o Fundador"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role.director"
msgstr "Director"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.company-size"
msgstr "¿Cuál es el tamaño de tu empresa?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.more-than-50"
msgstr "Más de 50"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.never-used-one"
msgstr "Ninguna"
msgid "questions.31-50"
msgstr "31-50"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.next"
msgstr "Siguiente"
msgid "questions.11-30"
msgstr "11-30"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.none"
msgstr "Ninguna"
msgid "questions.2-10"
msgstr "2-10"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.other"
msgstr "Otra (especifica)"
msgid "questions.freelancer"
msgstr "Soy autónomo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.personal-project"
msgstr "Estoy trabajando en un proyecto personal"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.step4-title"
msgstr "¿Por dónde te apetecería empezar?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.starting-ui"
msgstr "Diseñando el UX/UI de una aplicación"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.starting-wireframing"
msgstr "Wireframing"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.starting-prototyping"
msgstr "Prototipado"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.starting-ds"
msgstr "Creando sistemas de diseño"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.starting-code"
msgstr "Generando código a partir de diseños"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.step5-title"
msgstr "¿Cómo nos has descubierto?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.referer.youtube"
msgstr "YouTube"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.referer.event"
msgstr "Evento"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.referer.search"
msgstr "Buscador (Google, Yahoo, Bing)"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.referer.social"
msgstr "Redes sociales (X, LinkedIn, FB, ect)"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.referer.article"
msgstr "Artículo (Blog, Post, Newsletter)"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.lets-get-started"
msgstr "¡Empecemos!"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.next"
msgstr "Siguiente"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.other"
msgstr "Otra (especifica)"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.other-short"
msgstr "Otra"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.previous"
msgstr "Anterior"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.questions-how-are-you-planning-to-use-penpot"
msgstr "¿Qué uso piensas darle a Penpot?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role"
msgstr "¿Cuál es tu rol?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.select-option"
msgstr "Selecciona una opción"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.sketch"
msgstr "Sketch"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.some"
msgstr "Alguna"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.start"
msgstr "Comenzar"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.start-to-work-on-my-project"
msgstr "Comenzar a trabajar en mi proyecto"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.student-teacher"
msgstr "Estudiante o profesorado"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.team-size"
msgstr "¿De qué tamaño es tu equipo?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.test-penpot-to-see-if-its-a-fit-for-team"
msgstr "Valorar si Penpot es adecuado para mi equipo"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.try-out-before-using-penpot-on-premise"
msgstr "Probar Penpot antes de usarlo en una instalación propia"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.wireframes-user-journeys-flows-navigation-trees"
msgstr "prototipos, user journeys, flujos, árboles de navegación..."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-in-concept-ideas"
msgstr "Conceptualizar ideas"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.your-feedback-will-help-us"
msgstr ""
"Tus respuestas nos ayudarán a entender tus hábitos y preferencias, lo que "
"nos ayudará a continuar mejorando Penpot"
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs,
msgid "settings.detach"
msgstr "Desacoplar"

View file

@ -39,11 +39,6 @@ msgstr ""
"Hau PROBAK EGITEKO zerbitzua da. EZ ERABILI benetako lana egiteko, hemengo "
"proiektuak noizean behin ezabatu egingo dira."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Posta elektronikoa"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Pasahitza ahaztu duzu?"
@ -139,10 +134,6 @@ msgstr "Ez duzu konturik?"
msgid "auth.register-submit"
msgstr "Sortu kontua"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Doakoa da, Kode Irekia da"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Sortu kontua"
@ -154,12 +145,6 @@ msgstr "Diseinuak eta prototipoak egiteko kode irekiko soluzioa."
msgid "auth.terms-of-service"
msgstr "Zerbitzuaren erabilpen-baldintzak"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"Kontu berri bat sortzean, gure erabilpen-baldintzak eta pribatutasun "
"politika onartzen dituzu."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Egiaztapen mezu bat bidali dugu helbide honetara"

View file

@ -39,11 +39,6 @@ msgstr ""
"این یک سرویس آزمایشی است، برای کار واقعی استفاده نکنید، پروژه‌ها به صورت "
"دوره‌ای پاک می‌شوند."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "ایمیل"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "کلمه‌عبور را فراموش کرده‌اید؟"
@ -139,10 +134,6 @@ msgstr "هنوز حسابی ندارید؟"
msgid "auth.register-submit"
msgstr "ایجاد حساب‌کاربری"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "رایگان است، منبع‌باز است"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "ایجاد حساب"
@ -154,12 +145,6 @@ msgstr "راه حل منبع-باز برای طراحی و نمونه‌سازی
msgid "auth.terms-of-service"
msgstr "شرایط استفاده از خدمات"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"هنگام ایجاد یک حساب‌کاربری جدید، با شرایط خدمات و سیاست حفظ حریم‌خصوصی ما "
"موافقت می‌کنید."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "ما یک ایمیل تأیید ارسال کردیم به"

View file

@ -39,10 +39,6 @@ msgstr ""
"Tämä on DEMO versio, ÄLÄ KÄYTÄ oikeaan työhön, projektit tullaan määräajoin "
"poistamaan."
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Sähköposti"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Unohditko salasanasi?"
@ -138,11 +134,6 @@ msgstr "Ei käyttäjää?"
msgid "auth.register-submit"
msgstr "Luo uusi käyttäjä"
#: src/app/main/ui/auth/register.cljs
#, fuzzy
msgid "auth.register-subtitle"
msgstr "Se on ilmainen, sekä avointa lähdekoodia"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Luo uusi käyttäjä"
@ -154,10 +145,6 @@ msgstr "Avoimen lähdekoodin ratkaisu suunnitteluun ja prototyyppien valmistukse
msgid "auth.terms-of-service"
msgstr "Käyttöehdot"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr "Kun luot uuden käyttäjän, hyväksyt käyttöehdot ja tietosuojaselosteen."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Lähetimme vahvistussähköpostin osoitteeseen"

View file

@ -39,11 +39,6 @@ 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/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Teldupostur"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Gloymt loyniorðið?"
@ -137,10 +132,6 @@ msgstr "Onga konto enn?"
msgid "auth.register-submit"
msgstr "Stovna konto"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Tað er ókeypis, tað Open Source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Stovna eina konto"
@ -148,12 +139,6 @@ msgstr "Stovna eina konto"
msgid "auth.terms-of-service"
msgstr "Treytir"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"Tá ið tú gert eina nýggja kontu, játtar tú tænastu- og privatlívs-treytir "
"okkara."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Vit hava sent ein váttanar teldupost til"

View file

@ -39,11 +39,6 @@ msgstr ""
"Il sagit dun service de DÉMONSTRATION, NE L'UTILISEZ PAS pour du vrai "
"travail, les projets seront périodiquement supprimés."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Adresse email"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Mot de passe oublié?"
@ -151,10 +146,6 @@ msgstr "Pas encore de compte?"
msgid "auth.register-submit"
msgstr "Créer un compte"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Cest gratuit, cest Open Source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Créer un compte"
@ -166,12 +157,6 @@ msgstr "La solution Open Source pour la conception et le prototypage."
msgid "auth.terms-of-service"
msgstr "Conditions générales d'utilisation"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"En créant un compte, vous acceptez nos conditions générales d'utilisation "
"et notre politique de confidentialité."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Nous avons envoyé un e-mail de vérification à"

View file

@ -39,11 +39,6 @@ msgstr ""
"Este é un servizo de DEMOSTRACIÓN. NON O UTILICES para traballos reais, os "
"proxectos eliminanse periódicamente."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Correo electrónico"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Esqueciches o teu contrasinal?"
@ -139,10 +134,6 @@ msgstr "Ainda non tes unha conta?"
msgid "auth.register-submit"
msgstr "Crea unha conta"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "É libre, é Open Source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Crea unha conta"
@ -154,12 +145,6 @@ msgstr "A solución de código aberto para deseñar e crear prototipos."
msgid "auth.terms-of-service"
msgstr "Condicións de servizo"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"Ao crear unha nova conta, aceptas as nosas condicións de servizo e a "
"política de privacidade."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Enviamos un correo electrónico de verificación a"

View file

@ -38,11 +38,6 @@ msgid "auth.demo-warning"
msgstr ""
"זה שירות ניסיוני, לא להשתמש בו לעבודה אמתית, המיזמים יימחקו מדי פעם בפעם."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "דוא״ל"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "שכחת סיסמה?"
@ -170,10 +165,6 @@ msgstr "אין לך חשבון עדיין?"
msgid "auth.register-submit"
msgstr "יצירת חשבון"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "זה חינם, בקוד פתוח"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "יצירת חשבון"
@ -2447,10 +2438,6 @@ msgstr "2-10"
msgid "questions.31-50"
msgstr "31-50"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.a-lot"
msgstr "הרבה מהם"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.adobe-xd"
msgstr "Adobe XD"
@ -2459,22 +2446,6 @@ msgstr "Adobe XD"
msgid "questions.canva"
msgstr "Canva"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.describe-your-experience-working-on"
msgstr "מה יתאר הכי טוב את אופן השימוש שלך…"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.designer"
msgstr "עיצוב"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.developer"
msgstr "פיתוח"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.discover-more-about-penpot"
msgstr "היכרות מעמיקה יותר עם Penpot"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.figma"
msgstr "Figma"
@ -2507,14 +2478,6 @@ msgstr "להשאיר משוב למיזם הצוותי שלי"
msgid "questions.lets-get-started"
msgstr "מתחילים!"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.manager"
msgstr "ניהול מוצר או מיזם"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.marketing"
msgstr "שיווק"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.more-than-50"
msgstr "גדול מ־50"
@ -2527,10 +2490,6 @@ msgstr "אין"
msgid "questions.next"
msgstr "הבאה"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.none"
msgstr "כלום"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.other"
msgstr "אחר (נא לפרט)"
@ -2543,14 +2502,6 @@ msgstr "זאת עבודה על מיזם פרטי"
msgid "questions.previous"
msgstr "הקודמת"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.questions-how-are-you-planning-to-use-penpot"
msgstr "מהן התוכניות שלך בנוגע לשימוש ב־Penpot?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role"
msgstr "מה התפקיד שלך?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.select-option"
msgstr "בחירת אפשרות"
@ -2559,10 +2510,6 @@ msgstr "בחירת אפשרות"
msgid "questions.sketch"
msgstr "Sketch"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.some"
msgstr "חלק מהם"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.start"
msgstr "התחלה"

View file

@ -38,11 +38,6 @@ msgstr ""
"Ovo je DEMO usluga. NEMOJ KORISTITI za pravi rad. Projekti će se povremeno "
"brisati."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "E-mail"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Zaboravljena lozinka?"
@ -138,10 +133,6 @@ msgstr "Još nemaš račun?"
msgid "auth.register-submit"
msgstr "Stvori račun"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Besplatno je, Open Source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Stvori račun"
@ -153,12 +144,6 @@ msgstr "Open Source rješenje za dizajn i izradu prototipova."
msgid "auth.terms-of-service"
msgstr "Uvjeti pružanja usluge"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"Kada kreiraš novi račun, slažeš se s našim uvjetima pružanja usluge i "
"pravilima privatnosti."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Poslali smo e-mail za potvrdu na"

View file

@ -39,11 +39,6 @@ msgstr ""
"Ini layanan DEMO, JANGAN GUNAKAN untuk pekerjaan nyata, proyek-proyek ini "
"akan di hapus secara berkala."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Surel"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Lupa kata sandi?"
@ -164,10 +159,6 @@ msgstr "Belum punya akun?"
msgid "auth.register-submit"
msgstr "Buat akun baru"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Gratis dan Sumber Terbuka"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Buat akun baru"
@ -2428,10 +2419,6 @@ msgstr "210"
msgid "questions.31-50"
msgstr "3150"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.a-lot"
msgstr "Banyak"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.adobe-xd"
msgstr "Adobe XD"
@ -2461,42 +2448,18 @@ msgstr "Jelajahi lebih lanjut tentang Penpot"
msgid "questions.figma"
msgstr "Figma"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.founder"
msgstr "Pendiri/VP"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.freelancer"
msgstr "Saya seorang pekerja lepas"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.get-the-code-from-my-team-project"
msgstr "Dapatkan kode dari proyek tim saya "
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.interface-design-visual-assets-design-systems"
msgstr "... desain antarmuka, aset visual, sistem desain, dll."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.invision"
msgstr "InVision"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.leave-feedback-for-my-team-project"
msgstr "Tinggalkan masukan untuk proyek tim saya"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.lets-get-started"
msgstr "Mari kita mulai!"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.manager"
msgstr "Pengelola Produk atau Proyek"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.marketing"
msgstr "Pemasaran"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.more-than-50"
msgstr "Lebih dari 50"
@ -2505,10 +2468,6 @@ msgstr "Lebih dari 50"
msgid "questions.next"
msgstr "Berikutnya"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.none"
msgstr "Tidak ada"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.other"
msgstr "Lainnya (jelaskan)"
@ -2521,14 +2480,6 @@ msgstr "Saya mengerjakan proyek pribadi"
msgid "questions.previous"
msgstr "Sebelumnya"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.questions-how-are-you-planning-to-use-penpot"
msgstr "Apa rencana Anda menggunakan Penpot?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role"
msgstr "Apa peran Anda?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.select-option"
msgstr "Pilih opsi"
@ -2537,48 +2488,14 @@ msgstr "Pilih opsi"
msgid "questions.sketch"
msgstr "Sketch"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.some"
msgstr "Beberapa"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.start"
msgstr "Mulai"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.start-to-work-on-my-project"
msgstr "Mulai bekerja pada proyek saya"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.student-teacher"
msgstr "Siswa atau Guru"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.team-size"
msgstr "Seberapa besar tim Anda?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.test-penpot-to-see-if-its-a-fit-for-team"
msgstr "Coba Penpot untuk tim Anda "
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.try-out-before-using-penpot-on-premise"
msgstr "Coba sebelum menggunakan Penpot on-premise"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.wireframes-user-journeys-flows-navigation-trees"
msgstr "... gambar rangka, perjalanan & alur pengguna, pohon navigasi, dll."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-in-concept-ideas"
msgstr "Bekerja dalam ide konsep"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.your-feedback-will-help-us"
msgstr ""
"Masukan Anda akan membantu kami mengerti kebiasaan dan preferensi Anda "
"supaya kami dapat membuat Penpot sebuah alat yang berguna dan nyaman."
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs,
msgid "settings.detach"
msgstr "Copot"

View file

@ -39,11 +39,6 @@ msgstr ""
"Questo è un servizio di prova, non utilizzare per il lavoro reale, i "
"progetti verranno eliminati periodicamente."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Indirizzo e-mail"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Hai dimenticato la password?"
@ -135,10 +130,6 @@ msgstr "Non hai ancora un account?"
msgid "auth.register-submit"
msgstr "Crea un account"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "È gratuito, è Open Source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Crea un account"
@ -150,12 +141,6 @@ msgstr "La soluzione open source per il design e la prototipazione."
msgid "auth.terms-of-service"
msgstr "Condizioni di servizio"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"Creando un account, accetti le condizioni generali di servizio e "
"l'informativa sulla privacy."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Abbiamo inviato l'e-mail di verifica a"

View file

@ -35,10 +35,6 @@ msgstr "試してみませんか?"
msgid "auth.demo-warning"
msgstr "このサービスはデモ版です。実際の業務には使用しないでください。"
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "Eメール"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "パスワードを忘れましたか?"
@ -134,10 +130,6 @@ msgstr "アカウントをお持ちでない方はこちら"
msgid "auth.register-submit"
msgstr "アカウントを作成"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "フリーでオープンソースです"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "アカウントを作成"
@ -149,10 +141,6 @@ msgstr "デザインとプロトタイピングのためのオープンソース
msgid "auth.terms-of-service"
msgstr "利用規約"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr "サービスに登録するには、利用規約とプライバシーポリシーに同意する必要があります。"
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "認証メールを送信しました"

View file

@ -27,10 +27,6 @@ msgstr "데모 계정을 생성하세요"
msgid "auth.create-demo-profile"
msgstr "그냥 해볼까요?"
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "이메일"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "비밀번호를 잊어버리셨나요?"

View file

@ -41,10 +41,6 @@ msgstr ""
"Tai yra DEMONSTRACINĖ versija, NEKURKITE tikrų darbų, nes projektai "
"periodiškai - šalinami."
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "El. paštas"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Pamiršote slaptažodį?"
@ -141,10 +137,6 @@ msgstr "Dar neturite paskyros?"
msgid "auth.register-submit"
msgstr "Sukurti paskyrą"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Tai - nemokama, tai - atviras kodas"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Sukurti paskyrą"
@ -156,12 +148,6 @@ msgstr "Atviro kodo dizaino ir prototipų kūrimo sprendimas."
msgid "auth.terms-of-service"
msgstr "Paslaugų teikimo sąlygos"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"Kurdami naują paskyrą sutinkate su mūsų paslaugų teikimo sąlygomis ir "
"privatumo politika."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Išsiuntėme patvirtinimo el. laišką adresu"

View file

@ -40,11 +40,6 @@ 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/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "E-pasta adrese"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Aizmirsta parole?"
@ -162,10 +157,6 @@ msgstr "Vēl nav konta?"
msgid "auth.register-submit"
msgstr "Izveidot kontu"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Tas ir bezmaksas, tā ir atvērtā pirmkoda programmatūra"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Izveidot kontu"
@ -2485,10 +2476,6 @@ msgstr "2-10"
msgid "questions.31-50"
msgstr "31-50"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.a-lot"
msgstr "Plaša"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.adobe-xd"
msgstr "Adobe XD"
@ -2525,22 +2512,10 @@ msgstr "Dibinātājs/viceprezidents"
msgid "questions.freelancer"
msgstr "Esmu ārštātnieks(-ce)/Pašnodarbināts(-ā)"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.get-the-code-from-my-team-project"
msgstr "Koda iegūšana no manas grupas projekta "
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.interface-design-visual-assets-design-systems"
msgstr "... saskarnes dizains, vizuālie līdzekļi, dizaina sistēmas utt."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.invision"
msgstr "InVision"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.leave-feedback-for-my-team-project"
msgstr "Atstāt atsauksmes par manas grupas projektu"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.lets-get-started"
msgstr "Ķeramies pie darba!"
@ -2565,10 +2540,6 @@ msgstr "Neviens"
msgid "questions.next"
msgstr "Nākamais"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.none"
msgstr "Nav"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.other"
msgstr "Cits (jānorāda)"
@ -2597,10 +2568,6 @@ msgstr "Atlasīt iespēju"
msgid "questions.sketch"
msgstr "Sketch"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.some"
msgstr "Nelielā"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.start"
msgstr "Sākt"

View file

@ -39,10 +39,6 @@ msgstr ""
"ഇതൊരു ഡെമോ സേവനമാണ്, ഒരു യഥാർത്ഥ ജോലിക്ക് ഉപയോഗിക്കരുത്, പ്രൊജക്റ്റുകൾ "
"നിശ്ചിതസമയങ്ങളിൽ മായ്ക്കപ്പെടും."
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "ഇമെയിൽ"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "പാസ്‌വേഡ് മറന്നോ?"
@ -138,10 +134,6 @@ msgstr "ഇതുവരെ അക്കൗണ്ട് ഇല്ലേ?"
msgid "auth.register-submit"
msgstr "അക്കൗണ്ട് സൃഷ്ടിക്കുക"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "ഇത് സൗജന്യമാണ്, ഇത് ഓപ്പൺ സോഴ്സാണ്"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "അക്കൗണ്ട് സൃഷ്ടിക്കുക"
@ -153,12 +145,6 @@ msgstr "ഡിസൈനിങിനും പ്രോട്ടോടൈപ്
msgid "auth.terms-of-service"
msgstr "ഉപയോഗനിബന്ധനകൾ"
#: src/app/main/ui/auth/register.cljs
msgid "auth.terms-privacy-agreement"
msgstr ""
"ഒരു അക്കൗണ്ട് സൃഷ്ടിക്കുമ്പോൾ, നിങ്ങൾ ഞങ്ങളുടെ ഉപയോഗനിബന്ധനകളും "
"സ്വകാര്യതാനയവും അംഗീകരിക്കുന്നു."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "സാധൂകരിക്കാനുള്ള ഇമെയിൽ ഞങ്ങൾ അയച്ചിരിക്കുന്നു"

View file

@ -25,10 +25,6 @@ msgstr ""
"Ini adalah perkhidmatan DEMO, JANGAN GUNAKAN untuk kerja sebenar, projek "
"akan dipadam secara berkala."
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "E-mel"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Lupa kata laluan?"

View file

@ -15,11 +15,6 @@ msgstr ""
msgid "auth.confirm-password"
msgstr "Bekreft passord"
#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
#, fuzzy
msgid "auth.email"
msgstr "E-post"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Glemt passordet?"

View file

@ -39,11 +39,6 @@ msgstr ""
"Dit is een DEMO-service, GEBRUIK DIT NIET voor echt werk, de projecten "
"worden regelmatig gewist."
#: src/app/main/ui/auth/register.cljs,
#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs
msgid "auth.email"
msgstr "E-mail"
#: src/app/main/ui/auth/login.cljs
msgid "auth.forgot-password"
msgstr "Wachtwoord vergeten?"
@ -173,10 +168,6 @@ msgstr "Nog geen account?"
msgid "auth.register-submit"
msgstr "Account aanmaken"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-subtitle"
msgstr "Het is gratis, het is open source"
#: src/app/main/ui/auth/register.cljs
msgid "auth.register-title"
msgstr "Account aanmaken"
@ -2503,10 +2494,6 @@ msgstr "2-10"
msgid "questions.31-50"
msgstr "31-50"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.a-lot"
msgstr "Veel"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.adobe-xd"
msgstr "Adobe XD"
@ -2515,62 +2502,22 @@ msgstr "Adobe XD"
msgid "questions.canva"
msgstr "Canva"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.describe-your-experience-working-on"
msgstr "Hoe zou je je ervaring omschrijven voor het werken aan..."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.designer"
msgstr "Ontwerper"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.developer"
msgstr "Ontwikkelaar"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.discover-more-about-penpot"
msgstr "Meer over Penpot ontdekken"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.figma"
msgstr "Figma"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.founder"
msgstr "Oprichter/VP"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.freelancer"
msgstr "Ik ben een freelancer"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.get-the-code-from-my-team-project"
msgstr "Haal de code op van mijn teamproject "
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.interface-design-visual-assets-design-systems"
msgstr "... interfaceontwerp, visuel assets, design systems, enz."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.invision"
msgstr "InVision"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.leave-feedback-for-my-team-project"
msgstr "Laat feedback achter voor mijn teamproject"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.lets-get-started"
msgstr "Laten we beginnen!"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.manager"
msgstr "Product- of projectmanager"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.marketing"
msgstr "Marketing"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.more-than-50"
msgstr "Meer dan 50"
@ -2583,10 +2530,6 @@ msgstr "Geen"
msgid "questions.next"
msgstr "Volgende"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.none"
msgstr "Geen"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.other"
msgstr "Anders (namelijk…)"
@ -2599,14 +2542,6 @@ msgstr "Ik werk aan een persoonlijk project"
msgid "questions.previous"
msgstr "Vorige"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.questions-how-are-you-planning-to-use-penpot"
msgstr "Hoe ben je van plan Penpot te gebruiken?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.role"
msgstr "Wat is je rol?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.select-option"
msgstr "Selecteer een optie"
@ -2615,50 +2550,14 @@ msgstr "Selecteer een optie"
msgid "questions.sketch"
msgstr "Sketch"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.some"
msgstr "Sommige"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.start"
msgstr "Starten"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.start-to-work-on-my-project"
msgstr "Begin aan mijn project te werken"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.student-teacher"
msgstr "Student of docent"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.team-size"
msgstr "Hoe groot is je team?"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.test-penpot-to-see-if-its-a-fit-for-team"
msgstr "Penpot testen om te zien of het geschikt is voor het team "
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.try-out-before-using-penpot-on-premise"
msgstr "Probeer uit voordat je Penpot on-premise gebruikt"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.wireframes-user-journeys-flows-navigation-trees"
msgstr ""
"… draadmodellen, gebruikers journeys en stroomdiagrammen, navigatiebomen, "
"etc."
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.work-in-concept-ideas"
msgstr "Werken in conceptideeën"
#: src/app/main/ui/onboarding/questions.cljs
msgid "questions.your-feedback-will-help-us"
msgstr ""
"Jouw feedback helpt ons te begrijpen wat je gewoonten en voorkeuren zijn, "
"zodat we van Penpot een nuttig en plezierig hulpmiddel kunnen blijven maken."
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs,
msgid "settings.detach"
msgstr "Ontkoppelen"

Some files were not shown because too many files have changed in this diff Show more