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:
Amruth Pillai
2026-09-05 18:33:35 -07:00
committed by GitHub
parent 5850230f89
commit ab67831e4b
73 changed files with 1564 additions and 8 deletions
+10
View File
@@ -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]);
+16
View File
@@ -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"
+16
View File
@@ -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 "ለእይታ ተስማሚ"
+16
View File
@@ -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 "مناسب للعرض"
+16
View File
@@ -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"
+16
View File
@@ -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 "Побиране в изглед"
+16
View File
@@ -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 "দেখার জন্য উপযুক্ত"
+16
View File
@@ -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"
+16
View File
@@ -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í"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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 "Κατάλληλο για προβολή"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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 "مناسب برای مشاهده"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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 "התאמה לתצוגה"
+16
View File
@@ -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 "देखने के लिए उपयुक्त"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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 "見るのにふさわしい"
+16
View File
@@ -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 "សមនឹងមើល"
+16
View File
@@ -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 "ವೀಕ್ಷಿಸಲು ಹೊಂದಿಕೊಳ್ಳಿ"
+16
View File
@@ -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 "보기에 적합함"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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 "കാണാൻ അനുയോജ്യം"
+16
View File
@@ -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 "पाहण्यासाठी योग्य"
+16
View File
@@ -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"
+16
View File
@@ -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 "हेर्नको लागि उपयुक्त"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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 "ଦେଖିବା ପାଇଁ ଫିଟ୍"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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 "Подходит для просмотра"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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ë"
+16
View File
@@ -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 "Прилагоди приказу"
+16
View File
@@ -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"
+16
View File
@@ -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 "பார்வைக்கு ஏற்றது"
+16
View File
@@ -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 "వీక్షించడానికి సరిపోతుంది"
+16
View File
@@ -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 "เหมาะสำหรับรับชม"
+16
View File
@@ -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"
+16
View File
@@ -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 "Пристосувати до перегляду"
+16
View File
@@ -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"
+16
View File
@@ -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"
+16
View File
@@ -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 "适合观看"
+16
View File
@@ -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 "適合觀看"
+16
View File
@@ -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} />
+9
View File
@@ -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,
+14
View File
@@ -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);
+4
View File
@@ -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);
});
+1
View File
@@ -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 = {
+7 -1
View File
@@ -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() {
+1
View File
@@ -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 (°). |
+237
View File
@@ -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",
});
});