mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-10-03 10:13:47 +10:00
feat: add cover and contain picture fitting (#3461)
* feat: add picture fit options * fix: harden picture fit regressions * fix: address picture fit review findings
This commit is contained in:
@@ -121,6 +121,16 @@ describe("generateOpenApiSpec", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("accepts legacy input with omitted picture fit in the published request schema", async () => {
|
||||
const spec = (await generateSpec()) as GeneratedSpecView;
|
||||
|
||||
expect(spec.components?.schemas?.ResumeData).toMatchObject({
|
||||
properties: {
|
||||
picture: { required: expect.not.arrayContaining(["fit"]) },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("publishes the custom-section type and item correlation", async () => {
|
||||
const spec = (await generateSpec()) as GeneratedSpecView;
|
||||
const schema = z.fromJSONSchema(spec.components?.schemas?.ResumeData as Parameters<typeof z.fromJSONSchema>[0]);
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" en die volledige tydlyn daarvan sal permanent uitgevee word. Dit kan nie ongedaan gemaak word nie."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Inhoud"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Inhoud is in 'n rooster uitgelê."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Fins"
|
||||
msgid "Fireworks"
|
||||
msgstr "Vuurwerke"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Pas om te sien"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" እና ሙሉ የጊዜ መስመሩ በቋሚነት ይሰረዛሉ። ይህ ሊቀለበስ አይችልም።"
|
||||
@@ -1214,6 +1222,10 @@ msgstr "ይዘት"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "ይዘቱ በግሪድ ውስጥ ተቀምጧል።"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "ፊኒሽ"
|
||||
msgid "Fireworks"
|
||||
msgstr "ርችቶች"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "ለእይታ ተስማሚ"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "سيتم حذف \"{0} · {1}\" وسجله الزمني الكامل نهائيًا. لا يمكن التراجع عن ذلك."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "المحتوى"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "المحتوى منظّم في شبكة."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "الفنلندية"
|
||||
msgid "Fireworks"
|
||||
msgstr "ألعاب نارية"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "مناسب للعرض"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" və onun tam zaman xətti həmişəlik silinəcək. Bu əməliyyatı geri qaytarmaq mümkün deyil."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Məzmun"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Məzmun cədvəl (grid) şəklində düzülüb."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Fin"
|
||||
msgid "Fireworks"
|
||||
msgstr "Atəşfəşanlıq"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Görüntü üçün uyğunlaşdırın"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" и цялата му хронология ще бъдат изтрити завинаги. Това не може да бъде отменено."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Съдържание"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Съдържанието е оформено в мрежа."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Фински"
|
||||
msgid "Fireworks"
|
||||
msgstr "Фойерверки"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Побиране в изглед"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" এবং এর সম্পূর্ণ টাইমলাইন স্থায়ীভাবে মুছে ফেলা হবে। এটি পূর্বাবস্থায় ফেরানো যাবে না।"
|
||||
@@ -1214,6 +1222,10 @@ msgstr "বিষয়বস্তু"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "কনটেন্ট একটি গ্রিডে সাজানো আছে।"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "ফিনিশ"
|
||||
msgid "Fireworks"
|
||||
msgstr "আতশবাজি"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "দেখার জন্য উপযুক্ত"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" i tota la seva cronologia se suprimiran permanentment. Això no es pot desfer."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Contingut"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "El contingut està disposat en una graella."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finès"
|
||||
msgid "Fireworks"
|
||||
msgstr "Focs artificials"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Ajustar a la vista"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" a celá jeho časová osa budou trvale smazány. Tuto akci nelze vrátit zpět."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Obsah"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Obsah je uspořádán do mřížky."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finština"
|
||||
msgid "Fireworks"
|
||||
msgstr "Ohňostroj"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Přizpůsobit zobrazení"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" og hele dens tidslinje slettes permanent. Dette kan ikke fortrydes."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Indhold"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Indhold er opsat i et gitter."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finsk"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fyrværkeri"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Tilpas til visning"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" und der gesamte Verlauf werden dauerhaft gelöscht. Dies kann nicht rückgängig gemacht werden."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Inhalt"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Der Inhalt ist als Raster angeordnet."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finnisch"
|
||||
msgid "Fireworks"
|
||||
msgstr "Feuerwerk"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Passend zur Ansicht"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" και ολόκληρο το χρονολόγιό του θα διαγραφούν οριστικά. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Περιεχόμενο"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Το περιεχόμενο είναι διατεταγμένο σε πλέγμα."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Φινλανδικά"
|
||||
msgid "Fireworks"
|
||||
msgstr "Πυροτεχνήματα"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Κατάλληλο για προβολή"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Content"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Content is laid out in a grid."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finnish"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fireworks"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Fit to view"
|
||||
|
||||
@@ -1260,6 +1260,10 @@ msgstr "Content"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Content is laid out in a grid."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr "Contain"
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -1458,6 +1462,14 @@ msgstr "Count by source"
|
||||
msgid "Counted separately from the parse score. Coverage does not predict anything."
|
||||
msgstr "Counted separately from the parse score. Coverage does not predict anything."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr "Cover"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
#: src/features/applications/components/application-form-sheet.tsx
|
||||
#: src/features/cover-letters/library.tsx
|
||||
@@ -2505,6 +2517,10 @@ msgstr "Finnish"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fireworks"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr "Fit"
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Fit to view"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" y todo su historial se eliminarán permanentemente. Esta acción no se puede deshacer."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Contenido"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "El contenido está organizado en una cuadrícula."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finés"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fuegos artificiales"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Apto para visualización"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" و کل تاریخچه آن برای همیشه حذف میشوند. این کار قابل بازگشت نیست."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "محتوا"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "محتوا بهصورت شبکهای چیده شده است."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "فنلاندی"
|
||||
msgid "Fireworks"
|
||||
msgstr "آتش بازی"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "مناسب برای مشاهده"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" ja sen koko aikajana poistetaan pysyvästi. Tätä ei voi kumota."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Sisältö"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Sisältö on aseteltu ruudukkoon."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "suomi"
|
||||
msgid "Fireworks"
|
||||
msgstr "Ilotulitus"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Sovita näkymään"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" et tout son historique seront supprimés définitivement. Cette action est irréversible."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Contenu"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Le contenu est disposé en grille."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finnois"
|
||||
msgid "Fireworks"
|
||||
msgstr "Feux d'artifice"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Ajuster à la vue"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" וכל ציר הזמן שלו יימחקו לצמיתות. אי אפשר לבטל פעולה זו."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "תוכן"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "התוכן מסודר ברשת."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "פינית"
|
||||
msgid "Fireworks"
|
||||
msgstr "זיקוקים"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "התאמה לתצוגה"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" और इसकी पूरी टाइमलाइन स्थायी रूप से हटा दी जाएगी। इसे पूर्ववत नहीं किया जा सकता."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "सामग्री"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "सामग्री ग्रिड में व्यवस्थित है।"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "फिनिश"
|
||||
msgid "Fireworks"
|
||||
msgstr "आतिशबाजी"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "देखने के लिए उपयुक्त"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" és a teljes idővonala véglegesen törlődik. Ez nem vonható vissza."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Tartalom"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "A tartalom rácsszerkezetben van elrendezve."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "finn"
|
||||
msgid "Fireworks"
|
||||
msgstr "Tűzijáték"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Nézethez igazítva"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" dan seluruh linimasanya akan dihapus permanen. Ini tidak dapat dibatalkan."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Konten"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Konten ditata dalam bentuk kisi."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finlandia"
|
||||
msgid "Fireworks"
|
||||
msgstr "Kembang api"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Sesuai untuk dilihat"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" e tutta la sua cronologia verranno eliminati definitivamente. Questa azione non può essere annullata."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Contenuto"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Il contenuto è impaginato in una griglia."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finlandese"
|
||||
msgid "Fireworks"
|
||||
msgstr "fuochi d'artificio"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Adatto alla visione"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" とその全タイムラインは完全に削除されます。この操作は元に戻せません。"
|
||||
@@ -1214,6 +1222,10 @@ msgstr "コンテンツ"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "コンテンツがグリッドレイアウトになっています。"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "フィンランド語"
|
||||
msgid "Fireworks"
|
||||
msgstr "花火"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "見るのにふさわしい"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" និងប្រវត្តិពេលវេលាទាំងមូលរបស់វានឹងត្រូវបានលុបជាអចិន្ត្រៃយ៍។ មិនអាចត្រឡប់វិញបានទេ។"
|
||||
@@ -1214,6 +1222,10 @@ msgstr "មាតិកា"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "មាតិកាត្រូវបានរៀបចំតាមទម្រង់ក្រឡាចត្រង្គ។"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finnish"
|
||||
msgid "Fireworks"
|
||||
msgstr "កាំជ្រួច"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "សមនឹងមើល"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" ಮತ್ತು ಅದರ ಸಂಪೂರ್ಣ ಕಾಲರೇಖೆಯನ್ನು ಶಾಶ್ವತವಾಗಿ ಅಳಿಸಲಾಗುತ್ತದೆ. ಇದನ್ನು ಹಿಂತಿರುಗಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "ವಿಷಯ"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "ವಿಷಯವನ್ನು ಗ್ರಿಡ್ನಲ್ಲಿ ಜೋಡಿಸಲಾಗಿದೆ."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "ಫಿನ್ನಿಷ್"
|
||||
msgid "Fireworks"
|
||||
msgstr "ಪಟಾಕಿಗಳು"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "ವೀಕ್ಷಿಸಲು ಹೊಂದಿಕೊಳ್ಳಿ"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" 및 전체 타임라인이 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "내용"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "내용이 격자 형태로 배치되어 있습니다."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "핀란드어"
|
||||
msgid "Fireworks"
|
||||
msgstr "불꽃"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "보기에 적합함"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" ir visa jos laiko juosta bus visam laikui ištrinta. To negalima atšaukti."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Turinys"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Turinys sumaketuotas tinkleliu."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Suomių"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fejerverkai"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Pritaikyti peržiūrai"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" un visa tās laika skala tiks neatgriezeniski dzēsta. To nevar atsaukt."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Saturs"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Saturs ir izkārtots režģī."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Somu"
|
||||
msgid "Fireworks"
|
||||
msgstr "Uguņošana"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Piemērots skatam"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" എന്നതും അതിന്റെ പൂർണ്ണ ടൈംലൈനും ശാശ്വതമായി ഇല്ലാതാക്കും. ഇത് പഴയപടിയാക്കാൻ കഴിയില്ല."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "ഉള്ളടക്കം"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "ഉള്ളടക്കം ഒരു ഗ്രിഡിലായി ക്രമീകരിച്ചിരിക്കുന്നു."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "ഫിന്നിഷ്"
|
||||
msgid "Fireworks"
|
||||
msgstr "വെടിക്കെട്ട്"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "കാണാൻ അനുയോജ്യം"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" आणि त्याची पूर्ण टाइमलाइन कायमची हटवली जाईल. हे पूर्ववत करता येणार नाही."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "सामग्री"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "सामग्री ग्रिडमध्ये मांडलेली आहे."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "फिन्निश"
|
||||
msgid "Fireworks"
|
||||
msgstr "फटाके"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "पाहण्यासाठी योग्य"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" dan garis masa penuhnya akan dipadamkan secara kekal. Tindakan ini tidak boleh dibuat asal."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Kandungan"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Kandungan disusun dalam grid."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finland"
|
||||
msgid "Fireworks"
|
||||
msgstr "Bunga api"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Sesuai untuk dilihat"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" र यसको पूरा टाइमलाइन स्थायी रूपमा मेटिनेछ। यो पूर्ववत गर्न सकिँदैन।"
|
||||
@@ -1214,6 +1222,10 @@ msgstr "सामग्री"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "सामग्री ग्रिडमा सजाइएको छ।"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "फिनिस"
|
||||
msgid "Fireworks"
|
||||
msgstr "आतिशबाजी"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "हेर्नको लागि उपयुक्त"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" en de volledige tijdlijn worden permanent verwijderd. Dit kan niet ongedaan worden gemaakt."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Inhoud"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Inhoud is opgemaakt in een raster."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Fins"
|
||||
msgid "Fireworks"
|
||||
msgstr "Vuurwerk"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Geschikt om te bekijken"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" og hele tidslinjen slettes permanent. Dette kan ikke angres."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Innhold"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Innholdet er lagt ut i et rutenett."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finsk"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fyrverkeri"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Tilpasset til visning"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" ଏବଂ ଏହାର ସମ୍ପୂର୍ଣ୍ଣ ସମୟରେଖା ସ୍ଥାୟୀଭାବେ ହଟାଯିବ। ଏହା ପୁନଃଫେରାଇ ହେବ ନାହିଁ।"
|
||||
@@ -1214,6 +1222,10 @@ msgstr "ବିଷୟବସ୍ତୁ"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "ବିଷୟବସ୍ତୁ ଏକ ଗ୍ରିଡ୍ରେ ସଜ୍ଜିତ ଅଛି।"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "ଫିନିଶ୍"
|
||||
msgid "Fireworks"
|
||||
msgstr "ଆତସବାଜି"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "ଦେଖିବା ପାଇଁ ଫିଟ୍"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" i pełna oś czasu zostaną trwale usunięte. Tej operacji nie można cofnąć."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Treść"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Treść jest ułożona w siatce."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Fiński"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fajerwerki"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Dopasuj do widoku"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" e toda a sua linha do tempo serão excluídos permanentemente. Isso não pode ser desfeito."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Conteúdo"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "O conteúdo está organizado em uma grade."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finlandês"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fogos de artifício"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Adequado para visualização"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" e a sua cronologia completa serão eliminados permanentemente. Isto não pode ser anulado."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Conteúdo"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "O conteúdo está organizado numa grelha."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finlandês"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fogos de artifício"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Adequado para visualização"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" și istoricul său complet vor fi șterse definitiv. Această acțiune nu poate fi anulată."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Conținut"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Conținutul este dispus într-o grilă."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finlandeză"
|
||||
msgid "Fireworks"
|
||||
msgstr "Focuri de artificii"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Potrivire pentru vizualizare"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" и вся хронология будут безвозвратно удалены. Это действие нельзя отменить."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Содержание"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Содержимое расположено в виде сетки."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Финский"
|
||||
msgid "Fireworks"
|
||||
msgstr "Фейерверк"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Подходит для просмотра"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" a celá časová os budú natrvalo odstránené. Túto akciu nie je možné vrátiť späť."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Obsah"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Obsah je usporiadaný v mriežke."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Fínčina"
|
||||
msgid "Fireworks"
|
||||
msgstr "Ohňostroj"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Prispôsobiť zobrazeniu"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" in celotna časovnica bosta trajno izbrisana. Tega ni mogoče razveljaviti."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Vsebina"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Vsebina je razporejena v mreži."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finščina"
|
||||
msgid "Fireworks"
|
||||
msgstr "Ognjemet"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Prilagodi pogledu"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" dhe e gjithë kronologjia e saj do të fshihen përgjithmonë. Kjo nuk mund të zhbëhet."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Përmbajtja"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Përmbajtja është vendosur në një rrjetë."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finlandisht"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fishekzjarre"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "I përshtatshëm për t’u parë"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" и цела временска линија биће трајно избрисани. Ово не може да се опозове."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Садржај"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Садржај је распоређен у мрежи."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Фински"
|
||||
msgid "Fireworks"
|
||||
msgstr "Ватромет"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Прилагоди приказу"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" och hela tidslinjen tas bort permanent. Detta går inte att ångra."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Innehåll"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Innehållet är upplagt i ett rutnät."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Finska"
|
||||
msgid "Fireworks"
|
||||
msgstr "Fyrverkeri"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Anpassa för visning"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" மற்றும் அதன் முழு காலவரிசை நிரந்தரமாக நீக்கப்படும். இதை மீட்டெடுக்க முடியாது."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "உள்ளடக்கம்"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "உள்ளடக்கம் ஒரு கட்டமைப்பில் (கிரிட்) அமைக்கப்பட்டுள்ளது."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "ஃபின்னிஷ்"
|
||||
msgid "Fireworks"
|
||||
msgstr "பட்டாசுகள்"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "பார்வைக்கு ஏற்றது"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" మరియు దాని పూర్తి టైమ్లైన్ శాశ్వతంగా తొలగించబడతాయి. దీన్ని తిరిగి చేయలేరు."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "విషయం"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "కంటెంట్ గ్రిడ్లో అమర్చబడింది."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "ఫిన్నిష్"
|
||||
msgid "Fireworks"
|
||||
msgstr "బాణసంచా"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "వీక్షించడానికి సరిపోతుంది"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" และไทม์ไลน์ทั้งหมดจะถูกลบถาวร ไม่สามารถย้อนกลับได้"
|
||||
@@ -1214,6 +1222,10 @@ msgstr "เนื้อหา"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "เนื้อหาถูกจัดวางแบบกริด"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "ฟินแลนด์"
|
||||
msgid "Fireworks"
|
||||
msgstr "ดอกไม้ไฟ"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "เหมาะสำหรับรับชม"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" ve tüm zaman çizelgesi kalıcı olarak silinecek. Bu işlem geri alınamaz."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "İçerik"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "İçerik bir ızgara düzeninde yerleştirilmiş."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Fince"
|
||||
msgid "Fireworks"
|
||||
msgstr "Havai fişekler"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Görüntülemeye uygun"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" і всю хронологію буде назавжди видалено. Це не можна скасувати."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Зміст"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Вміст розташований у вигляді сітки."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Фінська"
|
||||
msgid "Fireworks"
|
||||
msgstr "Феєрверки"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Пристосувати до перегляду"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" va uning to‘liq xronologiyasi butunlay o‘chiriladi. Buni ortga qaytarib bo‘lmaydi."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Mazmun"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Kontent panjara koʻrinishida joylashtirilgan."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Fin tili"
|
||||
msgid "Fireworks"
|
||||
msgstr "Mushakbozlik"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Ko'rish uchun moslash"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" và toàn bộ dòng thời gian sẽ bị xóa vĩnh viễn. Không thể hoàn tác."
|
||||
@@ -1214,6 +1222,10 @@ msgstr "Nội dung"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "Nội dung được trình bày theo dạng lưới."
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "Tiếng Phần Lan"
|
||||
msgid "Fireworks"
|
||||
msgstr "Pháo hoa"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "Phù hợp để xem"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" 及其完整时间线将被永久删除。此操作无法撤销。"
|
||||
@@ -1214,6 +1222,10 @@ msgstr "内容"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "内容以网格形式排列。"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "芬兰语"
|
||||
msgid "Fireworks"
|
||||
msgstr "烟花"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "适合观看"
|
||||
|
||||
@@ -23,6 +23,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr "\"{0} · {1}\" 及其完整時間軸將被永久刪除。此操作無法復原。"
|
||||
@@ -1214,6 +1222,10 @@ msgstr "內容"
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr "內容以網格形式排列。"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2369,6 +2381,10 @@ msgstr "芬蘭語"
|
||||
msgid "Fireworks"
|
||||
msgstr "煙火"
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr "適合觀看"
|
||||
|
||||
@@ -18,6 +18,14 @@ msgstr ""
|
||||
#. placeholder {1}: application.company
|
||||
#. placeholder {1}: current.company
|
||||
#: src/features/applications/components/application-actions-menu.tsx
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore edges removed by an earlier crop."
|
||||
msgstr ""
|
||||
|
||||
#: src/features/applications/components/application-detail-sheet.tsx
|
||||
msgid "\"{0} · {1}\" and its full timeline will be permanently deleted. This can't be undone."
|
||||
msgstr ""
|
||||
@@ -1200,6 +1208,10 @@ msgstr ""
|
||||
msgid "Content is laid out in a grid."
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Contain"
|
||||
msgstr ""
|
||||
|
||||
#. Button label to continue to dashboard after successful registration
|
||||
#. Final action button after saving backup codes
|
||||
#. Primary action button to proceed to next step in two-factor setup
|
||||
@@ -2351,6 +2363,10 @@ msgstr ""
|
||||
msgid "Fireworks"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-sidebar/left/sections/picture.tsx
|
||||
msgid "Fit"
|
||||
msgstr ""
|
||||
|
||||
#: src/routes/builder/$resumeId/-components/dock.tsx
|
||||
msgid "Fit to view"
|
||||
msgstr ""
|
||||
|
||||
@@ -12,7 +12,7 @@ import { defaultResumeData } from "@reactive-resume/schema/resume/default";
|
||||
import { BasicsSectionBuilder } from "./basics";
|
||||
import { PictureSectionBuilder } from "./picture";
|
||||
|
||||
const state = vi.hoisted(() => ({ data: {} as ResumeData, update: vi.fn() }));
|
||||
const state = vi.hoisted(() => ({ data: {} as ResumeData, update: vi.fn(), uploadFile: vi.fn() }));
|
||||
|
||||
vi.mock("@/features/resume/builder/draft", () => ({
|
||||
useCurrentBuilderResumeSelector: (selector: (resume: { data: ResumeData }) => unknown) => selector(state),
|
||||
@@ -27,7 +27,7 @@ vi.mock("@/libs/tanstack-form", async () => {
|
||||
vi.mock("@/libs/orpc/client", () => ({
|
||||
orpc: {
|
||||
storage: {
|
||||
uploadFile: { mutationOptions: () => ({ mutationFn: vi.fn() }) },
|
||||
uploadFile: { mutationOptions: () => ({ mutationFn: state.uploadFile }) },
|
||||
deleteFile: { mutationOptions: () => ({ mutationFn: vi.fn() }) },
|
||||
},
|
||||
},
|
||||
@@ -47,6 +47,8 @@ beforeEach(() => {
|
||||
state.data = structuredClone(defaultResumeData);
|
||||
state.update.mockReset();
|
||||
state.update.mockImplementation((update: (draft: ResumeData) => void) => update(state.data));
|
||||
state.uploadFile.mockReset();
|
||||
state.uploadFile.mockResolvedValue({ url: "/uploads/picture.png" });
|
||||
});
|
||||
|
||||
function renderSection(children: ReactNode) {
|
||||
@@ -83,4 +85,69 @@ describe("builder field labels", () => {
|
||||
fireEvent.change(input, { target: { value: "144" } });
|
||||
await waitFor(() => expect(state.data.picture.size).toBe(144));
|
||||
});
|
||||
|
||||
it("persists named fit choices and previews contain without cropping", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderSection(<PictureSectionBuilder />);
|
||||
|
||||
expect(screen.getByRole("group", { name: "Fit" })).toBeInTheDocument();
|
||||
await user.click(screen.getByRole("button", { name: "Contain" }));
|
||||
await waitFor(() => expect(state.data.picture.fit).toBe("contain"));
|
||||
|
||||
const file = new File(["full-image"], "full.png", { type: "image/png" });
|
||||
await user.upload(screen.getAllByLabelText("Upload picture")[0] as HTMLInputElement, file);
|
||||
|
||||
await waitFor(() => expect(state.uploadFile).toHaveBeenCalledOnce());
|
||||
expect(state.uploadFile.mock.calls[0]?.[0]).toBe(file);
|
||||
const preview = screen.getByRole("button", { name: "Delete picture" }).querySelector("img");
|
||||
expect(preview).toBeInTheDocument();
|
||||
expect(getComputedStyle(preview as HTMLImageElement).objectFit).toBe("contain");
|
||||
expect(screen.queryByRole("dialog", { name: "Crop picture" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps cover uploads in cancelable crop flow", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderSection(<PictureSectionBuilder />);
|
||||
|
||||
const file = new File(["crop-image"], "crop.png", { type: "image/png" });
|
||||
await user.upload(screen.getAllByLabelText("Upload picture")[0] as HTMLInputElement, file);
|
||||
const dialog = screen.getByRole("dialog", { name: "Crop picture" });
|
||||
expect(dialog).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Cancel" }));
|
||||
expect(dialog).not.toBeInTheDocument();
|
||||
expect(state.uploadFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("retries the same full contain file after an upload error", async () => {
|
||||
state.uploadFile.mockRejectedValueOnce(new Error("Upload failed"));
|
||||
const user = userEvent.setup();
|
||||
renderSection(<PictureSectionBuilder />);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Contain" }));
|
||||
const file = new File(["full-image"], "full.png", { type: "image/png" });
|
||||
const input = screen.getAllByLabelText("Upload picture")[0] as HTMLInputElement;
|
||||
await user.upload(input, file);
|
||||
|
||||
await waitFor(() => expect(state.uploadFile).toHaveBeenCalledOnce());
|
||||
expect(state.data.picture.url).toBe("");
|
||||
expect(screen.queryByRole("dialog", { name: "Crop picture" })).not.toBeInTheDocument();
|
||||
await waitFor(() => expect(input.files).toHaveLength(0));
|
||||
|
||||
await user.upload(input, file);
|
||||
await waitFor(() => expect(state.uploadFile).toHaveBeenCalledTimes(2));
|
||||
expect(state.uploadFile.mock.calls[0]?.[0]).toBe(file);
|
||||
expect(state.uploadFile.mock.calls[1]?.[0]).toBe(file);
|
||||
});
|
||||
|
||||
it("disables fit and upload controls inside the builder lock fieldset", () => {
|
||||
renderSection(
|
||||
<fieldset disabled>
|
||||
<PictureSectionBuilder />
|
||||
</fieldset>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole("button", { name: "Contain" })).toBeDisabled();
|
||||
expect(screen.getAllByLabelText("Upload picture")[0]).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -94,7 +94,8 @@ function PicturePreviewControls({
|
||||
<img
|
||||
alt=""
|
||||
src={normalizedPictureUrl}
|
||||
className="fade-in relative z-10 size-full animate-in rounded-md object-cover transition-opacity group-hover/picture:opacity-20"
|
||||
style={{ objectFit: picture.fit }}
|
||||
className="fade-in relative z-10 size-full animate-in rounded-md transition-opacity group-hover/picture:opacity-20"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -143,12 +144,58 @@ function PicturePreviewControls({
|
||||
);
|
||||
}
|
||||
|
||||
type PictureGeometryFieldsProps = {
|
||||
type PictureFieldProps = {
|
||||
form: PictureSettingsForm;
|
||||
onAutoSave: () => void;
|
||||
};
|
||||
|
||||
function PictureGeometryFields({ form, onAutoSave }: PictureGeometryFieldsProps) {
|
||||
function PictureFitField({ form, onAutoSave }: PictureFieldProps) {
|
||||
return (
|
||||
<form.Field name="fit">
|
||||
{(field) => (
|
||||
<div className="space-y-1.5">
|
||||
<div className="font-medium text-sm">
|
||||
<Trans>Fit</Trans>
|
||||
</div>
|
||||
<ButtonGroup role="group" aria-label={t`Fit`} className="w-full">
|
||||
<Button
|
||||
type="button"
|
||||
variant={field.state.value === "cover" ? "default" : "outline"}
|
||||
aria-pressed={field.state.value === "cover"}
|
||||
className="flex-1"
|
||||
onClick={() => {
|
||||
field.handleChange("cover");
|
||||
onAutoSave();
|
||||
}}
|
||||
>
|
||||
<Trans>Cover</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant={field.state.value === "contain" ? "default" : "outline"}
|
||||
aria-pressed={field.state.value === "contain"}
|
||||
className="flex-1"
|
||||
onClick={() => {
|
||||
field.handleChange("contain");
|
||||
onAutoSave();
|
||||
}}
|
||||
>
|
||||
<Trans>Contain</Trans>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
<Trans>
|
||||
Cover crops the image to fill the frame. Contain shows the whole image. Reupload the original to restore
|
||||
edges removed by an earlier crop.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
);
|
||||
}
|
||||
|
||||
function PictureGeometryFields({ form, onAutoSave }: PictureFieldProps) {
|
||||
return (
|
||||
<>
|
||||
<form.Field name="size">
|
||||
@@ -519,7 +566,6 @@ function PictureSectionForm() {
|
||||
form.setFieldValue("url", url);
|
||||
handleAutoSave();
|
||||
toast.close(toastId);
|
||||
if (fileInputRef.current) fileInputRef.current.value = "";
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.add({
|
||||
@@ -534,12 +580,19 @@ function PictureSectionForm() {
|
||||
id: toastId,
|
||||
});
|
||||
},
|
||||
onSettled: () => {
|
||||
if (fileInputRef.current) fileInputRef.current.value = "";
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onUploadPicture = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
if (form.state.values.fit === "contain") {
|
||||
uploadPictureFile(file);
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the interactive crop step instead of uploading immediately.
|
||||
setCropState({ file, imageSrc: URL.createObjectURL(file) });
|
||||
@@ -666,6 +719,8 @@ function PictureSectionForm() {
|
||||
onUploadPicture={onUploadPicture}
|
||||
/>
|
||||
|
||||
<PictureFitField form={form} onAutoSave={handleAutoSave} />
|
||||
|
||||
<div className="grid @md:grid-cols-2 grid-cols-1 gap-4">
|
||||
<PictureGeometryFields form={form} onAutoSave={handleAutoSave} />
|
||||
|
||||
|
||||
@@ -160,6 +160,15 @@ The full JSON Schema for Reactive Resume follows. You can also fetch the latest
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the picture from the resume."
|
||||
},
|
||||
"fit": {
|
||||
"default": "cover",
|
||||
"description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"cover",
|
||||
"contain"
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload."
|
||||
|
||||
@@ -222,6 +222,7 @@ describe("parseReactiveResumeV4JSON — broad section mapping", () => {
|
||||
it("maps picture with border on", () => {
|
||||
expect(result.picture.url).toBe("https://example.com/pic.jpg");
|
||||
expect(result.picture.hidden).toBe(false);
|
||||
expect(result.picture.fit).toBe("cover");
|
||||
expect(result.picture.borderWidth).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
|
||||
@@ -248,6 +248,7 @@ export function parseReactiveResumeV4JSON(json: string): ResumeData {
|
||||
const transformed: ResumeData = {
|
||||
picture: {
|
||||
hidden: v4Data.basics.picture?.effects?.hidden ?? false,
|
||||
fit: "cover",
|
||||
url: v4Data.basics.picture?.url ?? "",
|
||||
size: clamp(v4Data.basics.picture?.size ?? 80, 32, 512),
|
||||
rotation: 0,
|
||||
|
||||
@@ -135,7 +135,7 @@ export function createBaseTemplateStyles({
|
||||
picture: {
|
||||
width: picture.size,
|
||||
height: picture.size,
|
||||
objectFit: "cover",
|
||||
objectFit: picture.fit,
|
||||
aspectRatio: picture.aspectRatio,
|
||||
borderRadius: picture.borderRadius,
|
||||
borderColor: rgbaStringToHex(picture.borderColor),
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createCanvas } from "@napi-rs/canvas";
|
||||
import { renderToBuffer } from "@react-pdf/renderer";
|
||||
import { act, createElement } from "react";
|
||||
import { defaultResumeData } from "@reactive-resume/schema/resume/default";
|
||||
import { ResumeDocument } from "../../document";
|
||||
import { rasterizePdf } from "../../semantic/test/rasterize-pdf";
|
||||
|
||||
type Fit = "cover" | "contain";
|
||||
type Orientation = "square" | "landscape" | "portrait";
|
||||
|
||||
const dimensions = {
|
||||
square: [300, 300],
|
||||
landscape: [400, 300],
|
||||
portrait: [300, 400],
|
||||
} as const;
|
||||
|
||||
const marker = {
|
||||
left: [255, 0, 0],
|
||||
right: [0, 255, 0],
|
||||
top: [0, 0, 255],
|
||||
bottom: [255, 255, 0],
|
||||
} as const;
|
||||
|
||||
const cropMarker = {
|
||||
start: [255, 128, 0],
|
||||
end: [0, 128, 128],
|
||||
} as const;
|
||||
|
||||
const frameColor = [255, 0, 255] as const;
|
||||
const pictureBounds = { left: 21, right: 170, top: 18, bottom: 167 } as const;
|
||||
|
||||
function markedImage(orientation: Orientation) {
|
||||
const [width, height] = dimensions[orientation];
|
||||
const canvas = createCanvas(width, height);
|
||||
const context = canvas.getContext("2d");
|
||||
context.fillStyle = "#ffffff";
|
||||
context.fillRect(0, 0, width, height);
|
||||
const strip = Math.round(Math.min(width, height) * 0.08);
|
||||
context.fillStyle = "#ff0000";
|
||||
context.fillRect(0, 0, strip, height);
|
||||
context.fillStyle = "#00ff00";
|
||||
context.fillRect(width - strip, 0, strip, height);
|
||||
context.fillStyle = "#0000ff";
|
||||
context.fillRect(strip, 0, width - strip * 2, strip);
|
||||
context.fillStyle = "#ffff00";
|
||||
context.fillRect(strip, height - strip, width - strip * 2, strip);
|
||||
if (orientation === "landscape") {
|
||||
context.fillStyle = "#ff8000";
|
||||
context.fillRect(50, 0, 5, height);
|
||||
context.fillStyle = "#008080";
|
||||
context.fillRect(width - 55, 0, 5, height);
|
||||
}
|
||||
if (orientation === "portrait") {
|
||||
context.fillStyle = "#ff8000";
|
||||
context.fillRect(0, 50, width, 5);
|
||||
context.fillStyle = "#008080";
|
||||
context.fillRect(0, height - 55, width, 5);
|
||||
}
|
||||
context.fillStyle = "#000000";
|
||||
context.fillRect(Math.floor(width / 2) - 3, strip, 6, height - strip * 2);
|
||||
context.fillRect(strip, Math.floor(height / 2) - 3, width - strip * 2, 6);
|
||||
return canvas.toDataURL("image/png");
|
||||
}
|
||||
|
||||
async function rasterPicture(
|
||||
orientation: Orientation,
|
||||
fit: Fit,
|
||||
stylesheet = "@version 1;",
|
||||
frame = { borderWidth: 6, shadowWidth: 8 },
|
||||
) {
|
||||
const data = structuredClone(defaultResumeData);
|
||||
data.basics.name = "Picture fit";
|
||||
data.metadata.typography.body.fontFamily = "Helvetica";
|
||||
data.metadata.typography.heading.fontFamily = "Helvetica";
|
||||
data.metadata.layout.pages = [{ fullWidth: false, main: [], sidebar: [] }];
|
||||
data.metadata.stylesheet = { mode: "semantic", source: { languageVersion: 1, text: stylesheet } };
|
||||
Object.assign(data.picture, {
|
||||
url: markedImage(orientation),
|
||||
hidden: false,
|
||||
fit,
|
||||
size: 100,
|
||||
aspectRatio: 1,
|
||||
borderRadius: 0,
|
||||
borderColor: "rgba(255, 0, 255, 1)",
|
||||
borderWidth: frame.borderWidth,
|
||||
shadowColor: "rgba(0, 255, 255, 1)",
|
||||
shadowWidth: frame.shadowWidth,
|
||||
});
|
||||
const element = createElement(ResumeDocument, { data, template: "onyx" }) as unknown as Parameters<
|
||||
typeof renderToBuffer
|
||||
>[0];
|
||||
let bytes = new Uint8Array();
|
||||
await act(async () => {
|
||||
bytes = new Uint8Array(await renderToBuffer(element));
|
||||
});
|
||||
const [page] = await rasterizePdf(bytes);
|
||||
if (!page) throw new Error("Missing rendered page");
|
||||
return page;
|
||||
}
|
||||
|
||||
function colorBounds(page: Awaited<ReturnType<typeof rasterPicture>>, color: readonly number[]) {
|
||||
const points: { x: number; y: number }[] = [];
|
||||
for (let index = 0; index < page.data.length; index += 4) {
|
||||
if (color.every((channel, offset) => Math.abs((page.data[index + offset] ?? -255) - channel) <= 2)) {
|
||||
points.push({ x: (index / 4) % page.width, y: Math.floor(index / 4 / page.width) });
|
||||
}
|
||||
}
|
||||
if (points.length === 0) return undefined;
|
||||
return {
|
||||
left: Math.min(...points.map(({ x }) => x)),
|
||||
right: Math.max(...points.map(({ x }) => x)),
|
||||
top: Math.min(...points.map(({ y }) => y)),
|
||||
bottom: Math.max(...points.map(({ y }) => y)),
|
||||
};
|
||||
}
|
||||
|
||||
type Bounds = NonNullable<ReturnType<typeof colorBounds>>;
|
||||
|
||||
function requiredColorBounds(
|
||||
page: Awaited<ReturnType<typeof rasterPicture>>,
|
||||
color: readonly number[],
|
||||
label: string,
|
||||
): Bounds {
|
||||
const bounds = colorBounds(page, color);
|
||||
expect(bounds, label).toBeDefined();
|
||||
if (!bounds) throw new Error(`Missing ${label}`);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
function mergedBounds(bounds: readonly Bounds[]): Bounds {
|
||||
return {
|
||||
left: Math.min(...bounds.map(({ left }) => left)),
|
||||
right: Math.max(...bounds.map(({ right }) => right)),
|
||||
top: Math.min(...bounds.map(({ top }) => top)),
|
||||
bottom: Math.max(...bounds.map(({ bottom }) => bottom)),
|
||||
};
|
||||
}
|
||||
|
||||
function expectBoundsWithinPixel(actual: Bounds, expected: Bounds) {
|
||||
for (const edge of ["left", "right", "top", "bottom"] as const) {
|
||||
expect(Math.abs(actual[edge] - expected[edge]), edge).toBeLessThanOrEqual(1);
|
||||
}
|
||||
}
|
||||
|
||||
function boundsCenter(bounds: Bounds) {
|
||||
return {
|
||||
x: (bounds.left + bounds.right) / 2,
|
||||
y: (bounds.top + bounds.bottom) / 2,
|
||||
};
|
||||
}
|
||||
|
||||
function expectSameCenter(actual: Bounds, expected: Bounds) {
|
||||
const actualCenter = boundsCenter(actual);
|
||||
const expectedCenter = boundsCenter(expected);
|
||||
expect(Math.abs(actualCenter.x - expectedCenter.x), "center x").toBeLessThanOrEqual(1);
|
||||
expect(Math.abs(actualCenter.y - expectedCenter.y), "center y").toBeLessThanOrEqual(1);
|
||||
}
|
||||
|
||||
function expectedContentBounds(borderWidth: number): Bounds {
|
||||
const inset = borderWidth * 1.5;
|
||||
return {
|
||||
left: pictureBounds.left + inset,
|
||||
right: pictureBounds.right - inset,
|
||||
top: pictureBounds.top + inset,
|
||||
bottom: pictureBounds.bottom - inset,
|
||||
};
|
||||
}
|
||||
|
||||
function expectedContainBounds(orientation: Orientation, borderWidth: number): Bounds {
|
||||
const content = expectedContentBounds(borderWidth);
|
||||
const contentWidth = content.right - content.left + 1;
|
||||
const contentHeight = content.bottom - content.top + 1;
|
||||
const [sourceWidth, sourceHeight] = dimensions[orientation];
|
||||
const scale = Math.min(contentWidth / sourceWidth, contentHeight / sourceHeight);
|
||||
const width = sourceWidth * scale;
|
||||
const height = sourceHeight * scale;
|
||||
const center = boundsCenter(content);
|
||||
return {
|
||||
left: center.x - (width - 1) / 2,
|
||||
right: center.x + (width - 1) / 2,
|
||||
top: center.y - (height - 1) / 2,
|
||||
bottom: center.y + (height - 1) / 2,
|
||||
};
|
||||
}
|
||||
|
||||
describe("picture fit geometry (#2782)", () => {
|
||||
it.each(["square", "landscape", "portrait"] as const)(
|
||||
"keeps every %s source edge visible and centered in contain mode",
|
||||
async (orientation) => {
|
||||
const page = await rasterPicture(orientation, "contain");
|
||||
const edges = Object.entries(marker).map(([name, color]) => requiredColorBounds(page, color, name));
|
||||
const image = mergedBounds(edges);
|
||||
expectBoundsWithinPixel(image, expectedContainBounds(orientation, 6));
|
||||
expectSameCenter(image, requiredColorBounds(page, frameColor, "frame"));
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
{ orientation: "landscape", retained: ["top", "bottom"], cropped: ["left", "right"] },
|
||||
{ orientation: "portrait", retained: ["left", "right"], cropped: ["top", "bottom"] },
|
||||
] as const)(
|
||||
"preserves centered cover crop geometry for $orientation sources",
|
||||
async ({ orientation, retained, cropped }) => {
|
||||
const page = await rasterPicture(orientation, "cover");
|
||||
for (const edge of retained) expect(colorBounds(page, marker[edge]), `${edge} retained`).toBeDefined();
|
||||
for (const edge of cropped) expect(colorBounds(page, marker[edge]), `${edge} cropped`).toBeUndefined();
|
||||
|
||||
const content = expectedContentBounds(6);
|
||||
const start = requiredColorBounds(page, cropMarker.start, "start crop marker");
|
||||
const end = requiredColorBounds(page, cropMarker.end, "end crop marker");
|
||||
const visibleImage = mergedBounds([
|
||||
...retained.map((edge) => requiredColorBounds(page, marker[edge], edge)),
|
||||
start,
|
||||
end,
|
||||
]);
|
||||
expectBoundsWithinPixel(visibleImage, content);
|
||||
expectSameCenter(mergedBounds([start, end]), content);
|
||||
|
||||
if (orientation === "landscape") {
|
||||
expect(Math.abs(start.left - content.left)).toBeLessThanOrEqual(1);
|
||||
expect(Math.abs(end.right - content.right)).toBeLessThanOrEqual(1);
|
||||
} else {
|
||||
expect(Math.abs(start.top - content.top)).toBeLessThanOrEqual(1);
|
||||
expect(Math.abs(end.bottom - content.bottom)).toBeLessThanOrEqual(1);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps square cover source edges uncropped", async () => {
|
||||
const page = await rasterPicture("square", "cover");
|
||||
const image = mergedBounds(Object.entries(marker).map(([name, color]) => requiredColorBounds(page, color, name)));
|
||||
expectBoundsWithinPixel(image, expectedContentBounds(6));
|
||||
expectSameCenter(image, requiredColorBounds(page, frameColor, "frame"));
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ borderWidth: 0, shadowWidth: 0 },
|
||||
{ borderWidth: 6, shadowWidth: 0 },
|
||||
{ borderWidth: 0, shadowWidth: 8 },
|
||||
{ borderWidth: 6, shadowWidth: 8 },
|
||||
])("retains every landscape edge with border $borderWidth and shadow $shadowWidth", async (frame) => {
|
||||
const page = await rasterPicture("landscape", "contain", "@version 1;", frame);
|
||||
const image = mergedBounds(Object.entries(marker).map(([name, color]) => requiredColorBounds(page, color, name)));
|
||||
expectBoundsWithinPixel(image, expectedContainBounds("landscape", frame.borderWidth));
|
||||
expectSameCenter(image, expectedContentBounds(frame.borderWidth));
|
||||
});
|
||||
|
||||
it("lets semantic object-fit cover override selected contain", async () => {
|
||||
const cover = await rasterPicture("landscape", "cover");
|
||||
const overridden = await rasterPicture("landscape", "contain", "@version 1; picture { object-fit: cover; }");
|
||||
expect([...overridden.data]).toEqual([...cover.data]);
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,7 @@ import { hasTemplatePicture } from "./picture";
|
||||
|
||||
const basePicture = {
|
||||
hidden: false,
|
||||
fit: "cover",
|
||||
url: "",
|
||||
size: 80,
|
||||
rotation: 0,
|
||||
|
||||
@@ -241,6 +241,20 @@ describe("pictureSchema", () => {
|
||||
expect(pictureSchema.safeParse(defaultResumeData.picture).success).toBe(true);
|
||||
});
|
||||
|
||||
it("defaults missing and invalid legacy fit values to cover", () => {
|
||||
const { fit: _fit, ...legacyPicture } = defaultResumeData.picture;
|
||||
|
||||
expect(pictureSchema.parse(legacyPicture).fit).toBe("cover");
|
||||
expect(pictureSchema.parse({ ...legacyPicture, fit: "stretch" }).fit).toBe("cover");
|
||||
});
|
||||
|
||||
it("preserves contain through parsing and JSON round-trip", () => {
|
||||
const picture = pictureSchema.parse({ ...defaultResumeData.picture, fit: "contain" });
|
||||
const roundTripped = pictureSchema.parse(JSON.parse(JSON.stringify(picture)));
|
||||
|
||||
expect(roundTripped.fit).toBe("contain");
|
||||
});
|
||||
|
||||
it("rejects size below 32", () => {
|
||||
const invalid = { ...defaultResumeData.picture, size: 16 };
|
||||
expect(pictureSchema.safeParse(invalid).success).toBe(false);
|
||||
|
||||
@@ -33,6 +33,10 @@ const itemWebsiteSchema = websiteSchema
|
||||
|
||||
export const pictureSchema = z.object({
|
||||
hidden: z.boolean().describe("Whether to hide the picture from the resume."),
|
||||
fit: z
|
||||
.enum(["cover", "contain"])
|
||||
.catch("cover")
|
||||
.describe("How the picture fits its frame: cover crops overflow, while contain preserves the whole image."),
|
||||
url: z
|
||||
.string()
|
||||
.describe(
|
||||
|
||||
@@ -20,6 +20,10 @@ describe("defaultResumeData", () => {
|
||||
expect(defaultResumeData.metadata.page.format).toBe("a4");
|
||||
});
|
||||
|
||||
it("uses cover picture fitting by default", () => {
|
||||
expect(defaultResumeData.picture.fit).toBe("cover");
|
||||
});
|
||||
|
||||
it("shows link underlines by default", () => {
|
||||
expect(defaultResumeData.metadata.page.hideLinkUnderline).toBe(false);
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ const section = (icon: string) => ({
|
||||
export const defaultResumeData: ResumeData = {
|
||||
picture: {
|
||||
hidden: false,
|
||||
fit: "cover",
|
||||
url: "",
|
||||
size: 80,
|
||||
rotation: 0,
|
||||
|
||||
@@ -21,6 +21,16 @@ describe("createResumeDataJsonSchema", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("accepts legacy input with omitted picture fit", () => {
|
||||
const schema = createResumeDataJsonSchema();
|
||||
|
||||
expect(schema).toMatchObject({
|
||||
properties: {
|
||||
picture: { required: expect.not.arrayContaining(["fit"]) },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("enforces the custom-section type and item correlation", () => {
|
||||
const generatedSchema = z.fromJSONSchema(createResumeDataJsonSchema());
|
||||
const valid = {
|
||||
|
||||
@@ -9,7 +9,13 @@ const toInputJsonSchema = (schema: z.ZodType) =>
|
||||
});
|
||||
|
||||
export function createResumeDataJsonSchema() {
|
||||
return toInputJsonSchema(resumeDataSchema);
|
||||
const schema = toInputJsonSchema(resumeDataSchema);
|
||||
const picture = schema.properties?.picture;
|
||||
// Zod's catch accepts an omitted fit at runtime but still marks the property required in JSON Schema.
|
||||
if (picture && typeof picture !== "boolean" && Array.isArray(picture.required)) {
|
||||
picture.required = picture.required.filter((property) => property !== "fit");
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
export function createCustomSectionItemJsonSchemas() {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { ResumeData } from "./data";
|
||||
export const sampleResumeData: ResumeData = {
|
||||
picture: {
|
||||
hidden: false,
|
||||
fit: "cover",
|
||||
url: "/photos/sample-picture.jpg",
|
||||
size: 100,
|
||||
rotation: 0,
|
||||
|
||||
@@ -35,6 +35,7 @@ Choose one coherent shape for each union value. Required fields are local to tha
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `picture` | `object` | yes | — | Configuration for photograph displayed on the resume |
|
||||
| `picture.hidden` | `boolean` | yes | — | Whether to hide the picture from the resume. |
|
||||
| `picture.fit` | `string` | no | enum: ["cover","contain"]; default: "cover" | How the picture fits its frame: cover crops overflow, while contain preserves the whole image. |
|
||||
| `picture.url` | `string` | yes | — | The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload. |
|
||||
| `picture.size` | `number` | yes | minimum: 32; maximum: 512 | The size of the picture to display on the resume, defined in points (pt). |
|
||||
| `picture.rotation` | `number` | yes | minimum: 0; maximum: 360 | The rotation of the picture to display on the resume, defined in degrees (°). |
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
import type { Page, TestInfo } from "@playwright/test";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { createRequire } from "node:module";
|
||||
import { Pool } from "pg";
|
||||
import { createSampleResumeFromDashboard, openSidebarSection } from "../fixtures/resume";
|
||||
import { expect, test } from "../fixtures/test";
|
||||
|
||||
type MarkerBounds = {
|
||||
left: number;
|
||||
right: number;
|
||||
top: number;
|
||||
bottom: number;
|
||||
count: number;
|
||||
};
|
||||
|
||||
type MarkerMetrics = Record<"red" | "green" | "blue" | "yellow", MarkerBounds | null>;
|
||||
|
||||
const requireRoot = createRequire(`${process.cwd()}/package.json`);
|
||||
|
||||
async function installPdfJsRoute(page: Page) {
|
||||
await page.route("**/__picture_pdfjs/*", async (route) => {
|
||||
const worker = new URL(route.request().url()).pathname.endsWith("worker.mjs");
|
||||
await route.fulfill({
|
||||
contentType: "text/javascript",
|
||||
path: requireRoot.resolve(`pdfjs-dist/legacy/build/${worker ? "pdf.worker.mjs" : "pdf.mjs"}`),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function markedLandscapePng(dataUrl: string) {
|
||||
return Buffer.from(dataUrl.slice(dataUrl.indexOf(",") + 1), "base64");
|
||||
}
|
||||
|
||||
function createMarkedLandscape(page: Page) {
|
||||
return page.evaluate(() => {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = 800;
|
||||
canvas.height = 600;
|
||||
const context = canvas.getContext("2d");
|
||||
if (!context) throw new Error("Canvas is unavailable");
|
||||
context.fillStyle = "#ffffff";
|
||||
context.fillRect(0, 0, 800, 600);
|
||||
context.fillStyle = "#ff0000";
|
||||
context.fillRect(0, 0, 64, 600);
|
||||
context.fillStyle = "#00ff00";
|
||||
context.fillRect(736, 0, 64, 600);
|
||||
context.fillStyle = "#0000ff";
|
||||
context.fillRect(64, 0, 672, 48);
|
||||
context.fillStyle = "#ffff00";
|
||||
context.fillRect(64, 552, 672, 48);
|
||||
context.fillStyle = "#000000";
|
||||
context.fillRect(394, 48, 12, 504);
|
||||
context.fillRect(64, 294, 672, 12);
|
||||
return canvas.toDataURL("image/png");
|
||||
});
|
||||
}
|
||||
|
||||
function markerMetricsFromPdf(page: Page, bytes: Uint8Array): Promise<MarkerMetrics> {
|
||||
return page.evaluate(async (pdfBytes) => {
|
||||
const pdfjs: typeof import("pdfjs-dist/legacy/build/pdf.mjs") = await import(
|
||||
`${location.origin}/__picture_pdfjs/pdf.mjs`
|
||||
);
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `${location.origin}/__picture_pdfjs/worker.mjs`;
|
||||
const task = pdfjs.getDocument({ data: Uint8Array.from(pdfBytes) });
|
||||
try {
|
||||
const pdfPage = await task.promise.then((pdf) => pdf.getPage(1));
|
||||
const viewport = pdfPage.getViewport({ scale: 1.5 });
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = Math.ceil(viewport.width);
|
||||
canvas.height = Math.ceil(viewport.height);
|
||||
const context = canvas.getContext("2d");
|
||||
if (!context) throw new Error("Canvas is unavailable");
|
||||
await pdfPage.render({ canvas, canvasContext: context, viewport, background: "white" }).promise;
|
||||
const pixels = context.getImageData(0, 0, canvas.width, canvas.height).data;
|
||||
const predicates = {
|
||||
red: (r: number, g: number, b: number) => r > 220 && g < 50 && b < 50,
|
||||
green: (r: number, g: number, b: number) => r < 50 && g > 220 && b < 50,
|
||||
blue: (r: number, g: number, b: number) => r < 50 && g < 50 && b > 220,
|
||||
yellow: (r: number, g: number, b: number) => r > 220 && g > 220 && b < 50,
|
||||
};
|
||||
const result = {} as Record<keyof typeof predicates, MarkerBounds | null>;
|
||||
for (const [name, predicate] of Object.entries(predicates) as Array<
|
||||
[keyof typeof predicates, (r: number, g: number, b: number) => boolean]
|
||||
>) {
|
||||
let left = Number.POSITIVE_INFINITY;
|
||||
let right = Number.NEGATIVE_INFINITY;
|
||||
let top = Number.POSITIVE_INFINITY;
|
||||
let bottom = Number.NEGATIVE_INFINITY;
|
||||
let count = 0;
|
||||
for (let index = 0; index < pixels.length; index += 4) {
|
||||
if (!predicate(pixels[index] ?? 0, pixels[index + 1] ?? 0, pixels[index + 2] ?? 0)) continue;
|
||||
const x = (index / 4) % canvas.width;
|
||||
const y = Math.floor(index / 4 / canvas.width);
|
||||
left = Math.min(left, x);
|
||||
right = Math.max(right, x);
|
||||
top = Math.min(top, y);
|
||||
bottom = Math.max(bottom, y);
|
||||
count++;
|
||||
}
|
||||
result[name] = count > 0 ? { left, right, top, bottom, count } : null;
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
await task.destroy();
|
||||
}
|
||||
}, Array.from(bytes));
|
||||
}
|
||||
|
||||
async function exportBuilderFile(page: Page, testInfo: TestInfo, buttonName: "Download JSON" | "Download PDF") {
|
||||
await openSidebarSection(page, "Export");
|
||||
await page.getByRole("button", { name: /Choose PDF, DOCX, Markdown, or JSON/ }).click();
|
||||
const pending = page.waitForEvent("download");
|
||||
await page.getByRole("button", { name: buttonName, exact: true }).click();
|
||||
const download = await pending;
|
||||
const path = testInfo.outputPath(download.suggestedFilename());
|
||||
await download.saveAs(path);
|
||||
await page.keyboard.press("Escape");
|
||||
return path;
|
||||
}
|
||||
|
||||
test("uploads and persists full Contain image with browser/server PDF parity", async ({
|
||||
authPage: page,
|
||||
account,
|
||||
}, info) => {
|
||||
test.setTimeout(120_000);
|
||||
await page.setViewportSize({ width: 1920, height: 950 });
|
||||
await installPdfJsRoute(page);
|
||||
await createSampleResumeFromDashboard(page, info);
|
||||
const resumeId = page.url().split("/").at(-1);
|
||||
if (!resumeId) throw new Error("Missing resume id");
|
||||
|
||||
await openSidebarSection(page, "Picture");
|
||||
await page.getByRole("button", { name: "Contain", exact: true }).click();
|
||||
await expect(page.getByRole("button", { name: "Contain", exact: true })).toHaveAttribute("aria-pressed", "true");
|
||||
await page.getByRole("button", { name: "Undo", exact: true }).click();
|
||||
await expect(page.getByRole("button", { name: "Cover", exact: true })).toHaveAttribute("aria-pressed", "true");
|
||||
await page.getByRole("button", { name: "Redo", exact: true }).click();
|
||||
await expect(page.getByRole("button", { name: "Contain", exact: true })).toHaveAttribute("aria-pressed", "true");
|
||||
const dataUrl = await createMarkedLandscape(page);
|
||||
const inputBytes = markedLandscapePng(dataUrl);
|
||||
await info.attach("marked-landscape.png", { body: inputBytes, contentType: "image/png" });
|
||||
await page.locator('#sidebar-picture input[type="file"]').setInputFiles({
|
||||
name: "marked-landscape.png",
|
||||
mimeType: "image/png",
|
||||
buffer: inputBytes,
|
||||
});
|
||||
await expect(page.getByRole("dialog", { name: "Crop picture" })).toHaveCount(0);
|
||||
const pictureUrl = page.locator("#sidebar-picture input[name=url]");
|
||||
await expect(pictureUrl).toHaveValue(/\/uploads\//);
|
||||
const sidebarPreview = page.getByRole("button", { name: "Delete picture" }).locator("img");
|
||||
await expect(sidebarPreview).toBeVisible();
|
||||
await expect(sidebarPreview).toHaveCSS("object-fit", "contain");
|
||||
const storedUrl = await pictureUrl.inputValue();
|
||||
const storedRequestUrl = storedUrl.startsWith("/uploads/") ? `/api${storedUrl}` : storedUrl;
|
||||
const storedResponse = await page.request.get(storedRequestUrl);
|
||||
expect(storedResponse.ok()).toBe(true);
|
||||
expect(storedResponse.headers()["content-type"]).toMatch(/^image\//);
|
||||
const storedBytes = await storedResponse.body();
|
||||
await info.attach("stored-landscape", { body: storedBytes, contentType: storedResponse.headers()["content-type"] });
|
||||
const storedGeometry = await page.evaluate(async (url) => {
|
||||
const image = new Image();
|
||||
image.src = url;
|
||||
await image.decode();
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
const context = canvas.getContext("2d");
|
||||
if (!context) throw new Error("Canvas is unavailable");
|
||||
context.drawImage(image, 0, 0);
|
||||
const sample = (x: number, y: number) => [...context.getImageData(x, y, 1, 1).data.slice(0, 3)];
|
||||
return {
|
||||
width: image.naturalWidth,
|
||||
height: image.naturalHeight,
|
||||
left: sample(Math.floor(image.naturalWidth * 0.02), Math.floor(image.naturalHeight / 2)),
|
||||
right: sample(Math.floor(image.naturalWidth * 0.98), Math.floor(image.naturalHeight / 2)),
|
||||
top: sample(Math.floor(image.naturalWidth / 4), Math.floor(image.naturalHeight * 0.02)),
|
||||
bottom: sample(Math.floor(image.naturalWidth / 4), Math.floor(image.naturalHeight * 0.98)),
|
||||
};
|
||||
}, storedRequestUrl);
|
||||
expect(storedGeometry).toMatchObject({ width: 800, height: 600 });
|
||||
expect(storedGeometry.left[0]).toBeGreaterThan(220);
|
||||
expect(storedGeometry.left[1]).toBeLessThan(50);
|
||||
expect(storedGeometry.right[0]).toBeLessThan(50);
|
||||
expect(storedGeometry.right[1]).toBeGreaterThan(220);
|
||||
expect(storedGeometry.top[2]).toBeGreaterThan(220);
|
||||
expect(storedGeometry.bottom[0]).toBeGreaterThan(220);
|
||||
expect(storedGeometry.bottom[1]).toBeGreaterThan(220);
|
||||
|
||||
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
||||
let slug = "";
|
||||
try {
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const result = await pool.query<{ data: { picture: { fit?: string; url?: string } }; slug: string }>(
|
||||
"select data, slug from resume where id = $1",
|
||||
[resumeId],
|
||||
);
|
||||
slug = result.rows[0]?.slug ?? "";
|
||||
return result.rows[0]?.data.picture;
|
||||
})
|
||||
.toMatchObject({ fit: "contain", url: storedUrl });
|
||||
|
||||
await page.getByRole("button", { name: "Cover", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Contain", exact: true }).click();
|
||||
expect(await pictureUrl.inputValue()).toBe(storedUrl);
|
||||
|
||||
await page.reload();
|
||||
await openSidebarSection(page, "Picture");
|
||||
await expect(page.getByRole("button", { name: "Contain", exact: true })).toHaveAttribute("aria-pressed", "true");
|
||||
await expect(page.locator("#sidebar-picture input[name=url]")).toHaveValue(storedUrl);
|
||||
|
||||
await pool.query("update resume set is_public = true, updated_at = now() where id = $1", [resumeId]);
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
|
||||
const jsonPath = await exportBuilderFile(page, info, "Download JSON");
|
||||
const exported = JSON.parse(await readFile(jsonPath, "utf8")) as { picture: { fit?: string; url?: string } };
|
||||
expect(exported.picture).toMatchObject({ fit: "contain", url: storedUrl });
|
||||
|
||||
const browserPdfPath = await exportBuilderFile(page, info, "Download PDF");
|
||||
const browserPdf = new Uint8Array(await readFile(browserPdfPath));
|
||||
const serverResponse = await page.request.get(`/api/resumes/${account.username}/${slug}/pdf`);
|
||||
expect(serverResponse.ok()).toBe(true);
|
||||
const serverPdf = new Uint8Array(await serverResponse.body());
|
||||
const browserMarkers = await markerMetricsFromPdf(page, browserPdf);
|
||||
const serverMarkers = await markerMetricsFromPdf(page, serverPdf);
|
||||
for (const name of ["red", "green", "blue", "yellow"] as const) {
|
||||
expect(browserMarkers[name]?.count).toBeGreaterThan(100);
|
||||
expect(serverMarkers[name]?.count).toBeGreaterThan(100);
|
||||
expect(serverMarkers[name]).toEqual(browserMarkers[name]);
|
||||
}
|
||||
await info.attach("pdf-marker-metrics", {
|
||||
body: JSON.stringify({ browserMarkers, serverMarkers }),
|
||||
contentType: "application/json",
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user