Add frame preset feature (#161)

This commit is contained in:
Estee Tey 2025-05-31 15:49:39 +08:00 committed by GitHub
parent a1df3be676
commit 6893839f58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 690 additions and 484 deletions

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Случаен избор на стил",
"No data!": "Няма данни!",
"Select language": "Избор на език",
"Select preset": "Избор на предварителна настройка",
"Select frame preset": "Избор на предварително зададена рамка",
"Select QR code preset": "Избор на предварителна настройка за QR код",
"GitHub repository for this project": "Хранилище на GitHub за този проект",
"Data to encode": "Данни за кодиране",
"data to encode e.g. a URL or a string": "данни за кодиране, напр. URL адрес или низ",
@ -42,7 +43,7 @@
"Last saved locally": "Зареждане от локално хранилище",
"Loaded from file": "Зареждане от файл",
"Copy QR Code to clipboard": "Копиране на QR кода в клипборда",
"Right click the image, and select 'Copy Image'": "Right click the image, and select 'Copy Image'",
"Right click the image, and select 'Copy Image'": "Кликнете с десния бутон върху изображението и изберете \"Копиране на изображението\".",
"Export as": "Експортиране като",
"Download QR Code as PNG": "Изтегляне на QR код като PNG",
"Download QR Code as SVG": "Изтегляне на QR код като SVG",
@ -130,6 +131,7 @@
"QR code settings": "Настройки на QR кода",
"Add frame": "Добавяне на рамка",
"Frame style": "Стил на рамката",
"Frame preset": "Предварителна настройка на кадъра",
"Text position": "Позиция на текста",
"Scan for more info": "Сканиране за повече информация",
"Text color": "Цвят на текста",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Náhodné styl",
"No data!": "Žádná data!",
"Select language": "Vyberte jazyk",
"Select preset": "Vybrat předvolbu",
"Select frame preset": "Výběr předvolby snímku",
"Select QR code preset": "Vyberte předvolbu QR kódu",
"GitHub repository for this project": "GitHub úložiště pro tento projekt",
"Data to encode": "Data k enkódování",
"data to encode e.g. a URL or a string": "data, která mají kódovat např. URL nebo řetězec",
@ -130,6 +131,7 @@
"QR code settings": "Nastavení QR kódu",
"Add frame": "Přidat rám",
"Frame style": "Styl rámu",
"Frame preset": "Předvolba rámečku",
"Text position": "Pozice textu",
"Scan for more info": "Vyhledat více informací",
"Text color": "Barva textu",

View File

@ -34,7 +34,8 @@
"Randomize style": "Tilfældig stil",
"No data!": "Ingen data!",
"Select language": "Vælg sprog",
"Select preset": "Vælg forudindstilling",
"Select frame preset": "Vælg forudindstillet ramme",
"Select QR code preset": "Vælg forudindstillet QR-kode",
"GitHub repository for this project": "GitHub repository for dette projekt",
"Data to encode": "Data der skal indkode",
"data to encode e.g. a URL or a string": "data til indkode f.eks. en URL eller en streng",
@ -130,6 +131,7 @@
"QR code settings": "Indstillinger for QR-kode",
"Add frame": "Tilføj ramme",
"Frame style": "Ramme stil",
"Frame preset": "Forudindstillet ramme",
"Text position": "Tekst position",
"Scan for more info": "Scan for mere info",
"Text color": "Tekst farve",

View File

@ -34,7 +34,8 @@
"Randomize style": "Zufälliger Stil",
"No data!": "Keine Daten!",
"Select language": "Sprache auswählen",
"Select preset": "Preset auswählen",
"Select frame preset": "Rahmenvoreinstellung auswählen",
"Select QR code preset": "QR-Code-Voreinstellung auswählen",
"GitHub repository for this project": "GitHub Repository für dieses Projekt",
"Data to encode": "Zu kodierende Daten",
"data to encode e.g. a URL or a string": "Daten zum Kodieren z.B. eine URL oder eine Zeichenkette",
@ -130,6 +131,7 @@
"QR code settings": "QR-Code-Einstellungen",
"Add frame": "Frame hinzufügen",
"Frame style": "Rahmenstil",
"Frame preset": "Voreinstellung Rahmen",
"Text position": "Textposition",
"Scan for more info": "Suche nach weiteren Informationen",
"Text color": "Textfarbe",

View File

@ -34,7 +34,8 @@
"Randomize style": "Τυχαίο στυλ",
"No data!": "Δεν υπάρχουν δεδομένα!",
"Select language": "Επιλέξτε γλώσσα",
"Select preset": "Επιλογή προεπιλογής",
"Select frame preset": "Επιλογή προεπιλογής καρέ",
"Select QR code preset": "Επιλέξτε προεπιλογή κώδικα QR",
"GitHub repository for this project": "Αποθετήριο GitHub για αυτό το έργο",
"Data to encode": "Δεδομένα για κωδικοποίηση",
"data to encode e.g. a URL or a string": "δεδομένα για κωδικοποίηση, π.χ. ένα URL ή μια συμβολοσειρά",
@ -130,6 +131,7 @@
"QR code settings": "Ρυθμίσεις QR code",
"Add frame": "Προσθήκη πλαισίου",
"Frame style": "Στυλ πλαισίου",
"Frame preset": "Προεπιλογή πλαισίου",
"Text position": "Θέση κειμένου",
"Scan for more info": "Σάρωση για περισσότερες πληροφορίες",
"Text color": "Χρώμα κειμένου",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Juhuslik stiil",
"No data!": "Andmed puuduvad!",
"Select language": "Valige keel",
"Select preset": "Valige eelseadistus",
"Select frame preset": "Valige kaadri eelseadistus",
"Select QR code preset": "Valige QR-koodi eelseadistus",
"GitHub repository for this project": "Selle projekti GitHubi repositoorium",
"Data to encode": "Kodeeritavad andmed",
"data to encode e.g. a URL or a string": "andmed, mida kodeerida, nt URL või string",
@ -42,7 +43,7 @@
"Last saved locally": "Laetud kohalikust ladustusest",
"Loaded from file": "Laetud failist",
"Copy QR Code to clipboard": "Kopeeri QR-kood lõikelauale",
"Right click the image, and select 'Copy Image'": "Right click the image, and select 'Copy Image'",
"Right click the image, and select 'Copy Image'": "Tehke pildil paremklõps ja valige 'Kopeeri pilt'.",
"Export as": "Eksportida kui",
"Download QR Code as PNG": "QR-koodi allalaadimine PNG-formaadis",
"Download QR Code as SVG": "Laadige QR-kood alla SVG-formaadis",
@ -130,6 +131,7 @@
"QR code settings": "QR-koodi seaded",
"Add frame": "Lisa raam",
"Frame style": "Raami stiil",
"Frame preset": "Raami eelseadistus",
"Text position": "Teksti asukoht",
"Scan for more info": "Skaneeri lisainfo saamiseks",
"Text color": "Teksti värvus",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Satunnaista tyyli",
"No data!": "Ei tietoja!",
"Select language": "Valitse kieli",
"Select preset": "Valitse esiasetus",
"Select frame preset": "Valitse kehyksen esiasetus",
"Select QR code preset": "Valitse QR-koodin esiasetus",
"GitHub repository for this project": "GitHub repo tälle projektille",
"Data to encode": "Tiedot, jotka koodataan",
"data to encode e.g. a URL or a string": "tiedot esim. URL-osoitteen tai merkkijonon koodaamiseen",
@ -130,6 +131,7 @@
"QR code settings": "QR-koodin asetukset",
"Add frame": "Lisää kehys",
"Frame style": "Kehyksen tyyli",
"Frame preset": "Kehyksen esiasetus",
"Text position": "Tekstin sijainti",
"Scan for more info": "Lue lisätietoja",
"Text color": "Tekstin väri",

View File

@ -34,7 +34,8 @@
"Randomize style": "Style aléatoire",
"No data!": "Aucune donnée !",
"Select language": "Sélectionner la langue",
"Select preset": "Sélectionner le préréglage",
"Select frame preset": "Sélectionner le préréglage du cadre",
"Select QR code preset": "Sélectionner le préréglage du code QR",
"GitHub repository for this project": "Dépôt GitHub pour ce projet",
"Data to encode": "Données à encoder",
"data to encode e.g. a URL or a string": "données à encoder par exemple une URL ou une chaîne de caractères",
@ -130,6 +131,7 @@
"QR code settings": "Paramètres du code QR",
"Add frame": "Ajouter un cadre",
"Frame style": "Style de cadre",
"Frame preset": "Cadre prédéfini",
"Text position": "Position du texte",
"Scan for more info": "Scanner pour plus d'informations",
"Text color": "Couleur du texte",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize stílus",
"No data!": "Nincs adat!",
"Select language": "Nyelv kiválasztása",
"Select preset": "Előbeállítás kiválasztása",
"Select frame preset": "Keret előbeállítás kiválasztása",
"Select QR code preset": "QR-kód előbeállítás kiválasztása",
"GitHub repository for this project": "GitHub tároló ehhez a projekthez",
"Data to encode": "Kódolandó adatok",
"data to encode e.g. a URL or a string": "kódolandó adatok, pl. URL vagy egy karakterlánc",
@ -42,7 +43,7 @@
"Last saved locally": "Helyi tárolóból betöltve",
"Loaded from file": "Fájlból betöltve",
"Copy QR Code to clipboard": "QR-kód másolása a vágólapra",
"Right click the image, and select 'Copy Image'": "Right click the image, and select 'Copy Image'",
"Right click the image, and select 'Copy Image'": "Kattintson a jobb gombbal a képre, és válassza a 'Kép másolása' lehetőséget.",
"Export as": "Exportálás",
"Download QR Code as PNG": "QR-kód letöltése PNG-ként",
"Download QR Code as SVG": "QR-kód letöltése SVG-ként",
@ -130,6 +131,7 @@
"QR code settings": "QR-kód beállítások",
"Add frame": "Keret hozzáadása",
"Frame style": "Keret stílus",
"Frame preset": "Előre beállított keret",
"Text position": "Szöveg pozíciója",
"Scan for more info": "További információért szkennelés",
"Text color": "Szöveg színe",

View File

@ -34,7 +34,8 @@
"Randomize style": "Mengacak gaya",
"No data!": "Tidak ada data!",
"Select language": "Pilih bahasa",
"Select preset": "Pilih preset",
"Select frame preset": "Memilih preset bingkai",
"Select QR code preset": "Memilih preset kode QR",
"GitHub repository for this project": "Repositori GitHub untuk proyek ini",
"Data to encode": "Data yang akan disandikan",
"data to encode e.g. a URL or a string": "data untuk dikodekan, misalnya URL atau string",
@ -42,7 +43,7 @@
"Last saved locally": "Dimuat dari penyimpanan lokal",
"Loaded from file": "Dimuat dari file",
"Copy QR Code to clipboard": "Salin Kode QR ke papan klip",
"Right click the image, and select 'Copy Image'": "Right click the image, and select 'Copy Image'",
"Right click the image, and select 'Copy Image'": "Klik kanan gambar, dan pilih 'Salin Gambar'",
"Export as": "Ekspor sebagai",
"Download QR Code as PNG": "Unduh Kode QR sebagai PNG",
"Download QR Code as SVG": "Unduh Kode QR sebagai SVG",
@ -130,6 +131,7 @@
"QR code settings": "Pengaturan kode QR",
"Add frame": "Tambahkan bingkai",
"Frame style": "Gaya bingkai",
"Frame preset": "Preset bingkai",
"Text position": "Posisi teks",
"Scan for more info": "Pindai untuk info lebih lanjut",
"Text color": "Warna teks",

View File

@ -34,7 +34,8 @@
"Randomize style": "Stile casuale",
"No data!": "Nessun dato!",
"Select language": "Seleziona lingua",
"Select preset": "Seleziona modello",
"Select frame preset": "Selezionare la preimpostazione della cornice",
"Select QR code preset": "Selezionare la preimpostazione del codice QR",
"GitHub repository for this project": "Repository GitHub per questo progetto",
"Data to encode": "Dati da codificare",
"data to encode e.g. a URL or a string": "dati da codificare, ad esempio un URL o una stringa",
@ -130,6 +131,7 @@
"QR code settings": "Impostazioni codice QR",
"Add frame": "Aggiungi cornice",
"Frame style": "Stile cornice",
"Frame preset": "Cornice preimpostata",
"Text position": "Posizione testo",
"Scan for more info": "Scansiona per saperne di più",
"Text color": "Colore testo",

View File

@ -34,7 +34,8 @@
"Randomize style": "ランダムなスタイル",
"No data!": "データがありません!",
"Select language": "言語を選択",
"Select preset": "プリセットを選択",
"Select frame preset": "フレームプリセットを選択",
"Select QR code preset": "QRコードのプリセットを選択",
"GitHub repository for this project": "このプロジェクトの GitHub リポジトリ",
"Data to encode": "エンコードするデータ",
"data to encode e.g. a URL or a string": "エンコードするデータ 例: URL または 文字列",
@ -130,6 +131,7 @@
"QR code settings": "QRコードの設定",
"Add frame": "枠を追加",
"Frame style": "枠のスタイル",
"Frame preset": "フレームプリセット",
"Text position": "テキストの位置",
"Scan for more info": "詳細情報をスキャン",
"Text color": "テキストの色",

View File

@ -34,7 +34,8 @@
"Randomize style": "스타일 무작위화",
"No data!": "데이터가 없습니다!",
"Select language": "언어 선택",
"Select preset": "사전 설정 선택",
"Select frame preset": "프레임 프리셋 선택",
"Select QR code preset": "QR코드 사전 설정 선택",
"GitHub repository for this project": "이 프로젝트의 GitHub 리포지토리",
"Data to encode": "인코딩할 데이터",
"data to encode e.g. a URL or a string": "인코딩할 데이터(예: URL 또는 문자열)",
@ -42,7 +43,7 @@
"Last saved locally": "로컬 스토리지에서 로드",
"Loaded from file": "파일에서 로드",
"Copy QR Code to clipboard": "QR 코드를 클립보드에 복사",
"Right click the image, and select 'Copy Image'": "Right click the image, and select 'Copy Image'",
"Right click the image, and select 'Copy Image'": "이미지를 마우스 오른쪽 버튼으로 클릭하고 '이미지 복사'를 선택합니다.",
"Export as": "다른 이름으로 내보내기",
"Download QR Code as PNG": "QR 코드를 PNG로 다운로드",
"Download QR Code as SVG": "QR 코드를 SVG로 다운로드",
@ -130,6 +131,7 @@
"QR code settings": "QR코드 설정",
"Add frame": "프레임 추가",
"Frame style": "프레임 스타일",
"Frame preset": "프레임 사전 설정",
"Text position": "텍스트 위치",
"Scan for more info": "자세한 정보 보기",
"Text color": "텍스트 색상",

View File

@ -34,7 +34,8 @@
"Randomize style": "Atsitiktinės atrankos stilius",
"No data!": "Nėra duomenų!",
"Select language": "Pasirinkite kalbą",
"Select preset": "Pasirinkite išankstinį nustatymą",
"Select frame preset": "Pasirinkite išankstinį kadro nustatymą",
"Select QR code preset": "Pasirinkite išankstinį QR kodo nustatymą",
"GitHub repository for this project": "Šio projekto \"GitHub\" saugykla",
"Data to encode": "Koduojami duomenys",
"data to encode e.g. a URL or a string": "koduojami duomenys, pvz., URL arba eilutė.",
@ -42,7 +43,7 @@
"Last saved locally": "Įkelta iš vietinės saugyklos",
"Loaded from file": "Įkelta iš failo",
"Copy QR Code to clipboard": "QR kodo kopijavimas į iškarpinę",
"Right click the image, and select 'Copy Image'": "Right click the image, and select 'Copy Image'",
"Right click the image, and select 'Copy Image'": "Dešiniuoju pelės mygtuku spustelėkite paveikslėlį ir pasirinkite \"Kopijuoti paveikslėlį\".",
"Export as": "Eksportuoti kaip",
"Download QR Code as PNG": "Atsisiųsti QR kodą kaip PNG",
"Download QR Code as SVG": "Atsisiųsti QR kodą kaip SVG",
@ -130,6 +131,7 @@
"QR code settings": "QR kodo nustatymai",
"Add frame": "Pridėti rėmelį",
"Frame style": "Rėmo stilius",
"Frame preset": "Išankstinis kadro nustatymas",
"Text position": "Teksto padėtis",
"Scan for more info": "Nuskaitykite, kad gautumėte daugiau informacijos",
"Text color": "Teksto spalva",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Willekeurige stijl",
"No data!": "Geen gegevens!",
"Select language": "Taal selecteren",
"Select preset": "Selecteer voorinstelling",
"Select frame preset": "Voorkeursframe kiezen",
"Select QR code preset": "Selecteer QR code voorinstelling",
"GitHub repository for this project": "GitHub repository voor dit project",
"Data to encode": "Gegevens om te coderen",
"data to encode e.g. a URL or a string": "gegevens om te coderen, bijvoorbeeld een URL of een tekenreeks",
@ -130,6 +131,7 @@
"QR code settings": "Instellingen QR-code",
"Add frame": "Frame toevoegen",
"Frame style": "Frame stijl",
"Frame preset": "Vooringesteld frame",
"Text position": "Tekst positie",
"Scan for more info": "Zoeken naar meer informatie",
"Text color": "Tekst kleur",

View File

@ -34,7 +34,8 @@
"Randomize style": "Tilfeldig stil",
"No data!": "Ingen data!",
"Select language": "Velg språk",
"Select preset": "Velg forhåndsinnstilling",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub område for dette prosjektet",
"Data to encode": "Data for å kode",
"data to encode e.g. a URL or a string": "data å kode for eksempel en URL eller en streng",
@ -130,6 +131,7 @@
"QR code settings": "QR-kode innstillinger",
"Add frame": "Legg til bilde",
"Frame style": "Ramme stil",
"Frame preset": "Frame preset",
"Text position": "Posisjon for tekst",
"Scan for more info": "Søk etter mer info",
"Text color": "Tekst farge",

View File

@ -34,7 +34,8 @@
"Randomize style": "Losowy styl",
"No data!": "Brak danych!",
"Select language": "Wybierz język",
"Select preset": "Wybierz ustawienie wstępne",
"Select frame preset": "Wybór wstępnego ustawienia ramki",
"Select QR code preset": "Wybierz wstępne ustawienie kodu QR",
"GitHub repository for this project": "Repozytorium GitHub dla tego projektu",
"Data to encode": "Dane do enkodowania",
"data to encode e.g. a URL or a string": "dane do kodowania, np. adres URL lub ciąg znaków",
@ -130,6 +131,7 @@
"QR code settings": "Ustawienia kodu QR",
"Add frame": "Dodaj ramkę",
"Frame style": "Styl ramki",
"Frame preset": "Wstępne ustawienie ramki",
"Text position": "Pozycja tekstu",
"Scan for more info": "Skanuj po więcej informacji",
"Text color": "Kolor tekstu",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomizar estilo",
"No data!": "Sem dados!",
"Select language": "Selecione o idioma",
"Select preset": "Selecionar predefinição",
"Select frame preset": "Selecionar predefinição de moldura",
"Select QR code preset": "Selecionar código QR predefinido",
"GitHub repository for this project": "Repositório GitHub para este projeto",
"Data to encode": "Dados para codificar",
"data to encode e.g. a URL or a string": "dados para codificar, por exemplo, uma URL ou uma string",
@ -130,6 +131,7 @@
"QR code settings": "Configurações de código QR",
"Add frame": "Adicionar quadro",
"Frame style": "Estilo de quadro",
"Frame preset": "Quadro pré-definido",
"Text position": "Posição do texto",
"Scan for more info": "Procurar mais informações",
"Text color": "Cor do texto",

View File

@ -34,7 +34,8 @@
"Randomize style": "Stil aleator",
"No data!": "Nici o dată!",
"Select language": "Selectați limba",
"Select preset": "Selectați presetarea",
"Select frame preset": "Selectați presetarea cadrului",
"Select QR code preset": "Selectați presetarea codului QR",
"GitHub repository for this project": "Depozitul GitHub pentru acest proiect",
"Data to encode": "Date de codificat",
"data to encode e.g. a URL or a string": "date pentru codificare ex. un URL sau un şir de caractere",
@ -130,6 +131,7 @@
"QR code settings": "Setări cod QR",
"Add frame": "Adaugă cadru",
"Frame style": "Stil cadru",
"Frame preset": "Cadru prestabilit",
"Text position": "Poziție text",
"Scan for more info": "Scanare pentru mai multe informații",
"Text color": "Culoare text",

View File

@ -34,7 +34,8 @@
"Randomize style": "Случайный стиль",
"No data!": "Нет данных!",
"Select language": "Выберите язык",
"Select preset": "Выберите пресет",
"Select frame preset": "Выберите предустановку кадра",
"Select QR code preset": "Выберите предварительную настройку QR-кода",
"GitHub repository for this project": "GitHub репозиторий для этого проекта",
"Data to encode": "Данные для кодирования",
"data to encode e.g. a URL or a string": "для кодирования например URL или строка",
@ -130,6 +131,7 @@
"QR code settings": "Настройки QR-кода",
"Add frame": "Добавить кадр",
"Frame style": "Стиль кадра",
"Frame preset": "Предварительная настройка кадра",
"Text position": "Позиция текста",
"Scan for more info": "Сканировать для дополнительной информации",
"Text color": "Цвет текста",

View File

@ -34,7 +34,8 @@
"Randomize style": "Náhodný štýl",
"No data!": "Žiadne údaje!",
"Select language": "Výber jazyka",
"Select preset": "Vyberte predvoľbu",
"Select frame preset": "Výber predvoľby rámu",
"Select QR code preset": "Vyberte predvoľbu QR kódu",
"GitHub repository for this project": "Úložisko GitHub pre tento projekt",
"Data to encode": "Údaje na kódovanie",
"data to encode e.g. a URL or a string": "údaje na zakódovanie, napr. URL alebo reťazec",
@ -42,7 +43,7 @@
"Last saved locally": "Načítané z miestneho úložiska",
"Loaded from file": "Načítané zo súboru",
"Copy QR Code to clipboard": "Skopírovanie kódu QR do schránky",
"Right click the image, and select 'Copy Image'": "Right click the image, and select 'Copy Image'",
"Right click the image, and select 'Copy Image'": "Kliknite pravým tlačidlom myši na obrázok a vyberte možnosť \"Kopírovať obrázok\".",
"Export as": "Exportovať ako",
"Download QR Code as PNG": "Stiahnutie kódu QR ako PNG",
"Download QR Code as SVG": "Stiahnutie kódu QR ako SVG",
@ -130,6 +131,7 @@
"QR code settings": "Nastavenia kódu QR",
"Add frame": "Pridať rám",
"Frame style": "Štýl rámu",
"Frame preset": "Prednastavenie rámu",
"Text position": "Pozícia textu",
"Scan for more info": "Skenovanie pre viac informácií",
"Text color": "Farba textu",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "Slumpa stil",
"No data!": "Inga data!",
"Select language": "Välj språk",
"Select preset": "Välj förval",
"Select frame preset": "Välj förinställd ram",
"Select QR code preset": "Välj förinställd QR-kod",
"GitHub repository for this project": "GitHub utvecklingskatalog för detta projekt",
"Data to encode": "Data att koda",
"data to encode e.g. a URL or a string": "data att koda t.ex. en URL eller en sträng",
@ -130,6 +131,7 @@
"QR code settings": "Inställningar för QR-kod",
"Add frame": "Lägg till ram",
"Frame style": "Ram stil",
"Frame preset": "Förinställd ram",
"Text position": "Text position",
"Scan for more info": "Sök efter mer information",
"Text color": "Text färg",

View File

@ -34,7 +34,8 @@
"Randomize style": "สุ่มรูปแบบ",
"No data!": "ไม่มีข้อมูล!",
"Select language": "เลือกภาษา",
"Select preset": "เลือกพรีเซ็ตกำหนดเอาไว้",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "ข้อมูลที่ต้องการเข้ารหัส",
"data to encode e.g. a URL or a string": "ข้อมูลที่ต้องการเข้ารหัส เช่น URL หรือ ข้อความ",
@ -130,6 +131,7 @@
"QR code settings": "การตั้งค่า QR Code",
"Add frame": "เพิ่มกรอบรูป",
"Frame style": "รูปแบบกรอบรูป",
"Frame preset": "Frame preset",
"Text position": "ตำแหน่งข้อความ",
"Scan for more info": "สแกนเพื่อดูข้อมูลเพิ่มเติม",
"Text color": "สีข้อความ",

View File

@ -34,7 +34,8 @@
"Randomize style": "Stili rastgele ayarla",
"No data!": "Veri yok!",
"Select language": "Dil seçin",
"Select preset": "Ön ayar seçme",
"Select frame preset": "Çerçeve ön ayarını seçin",
"Select QR code preset": "QR kodu ön ayarını seçin",
"GitHub repository for this project": "Bu proje için GitHub deposu",
"Data to encode": "Kodlanacak veriler",
"data to encode e.g. a URL or a string": "kodlanacak veri, örneğin bir URL veya bir dize",
@ -42,7 +43,7 @@
"Last saved locally": "Yerel depodan yüklendi",
"Loaded from file": "Dosyadan yüklendi",
"Copy QR Code to clipboard": "QR Kodunu panoya kopyalama",
"Right click the image, and select 'Copy Image'": "Right click the image, and select 'Copy Image'",
"Right click the image, and select 'Copy Image'": "Görüntüye sağ tıklayın ve 'Görüntüyü Kopyala'yı seçin",
"Export as": "Olarak dışa aktar",
"Download QR Code as PNG": "QR Kodunu PNG Olarak İndirin",
"Download QR Code as SVG": "QR Kodunu SVG Olarak İndirin",
@ -130,6 +131,7 @@
"QR code settings": "QR kodu ayarları",
"Add frame": "Çerçeve ekleyin",
"Frame style": "Çerçeve stili",
"Frame preset": "Çerçeve ön ayarı",
"Text position": "Metin konumu",
"Scan for more info": "Daha fazla bilgi için tarayın",
"Text color": "Metin rengi",

View File

@ -34,7 +34,8 @@
"Randomize style": "Випадковий стиль",
"No data!": "Дані відсутні!",
"Select language": "Вибрати мову",
"Select preset": "Обрати пресет",
"Select frame preset": "Виберіть попередню установку кадру",
"Select QR code preset": "Виберіть попередньо встановлений QR-код",
"GitHub repository for this project": "GitHub репозиторій для цього проекту",
"Data to encode": "Дані для кодування",
"data to encode e.g. a URL or a string": "дані для кодування, напр. URL або рядок",
@ -130,6 +131,7 @@
"QR code settings": "Параметри QR-коду",
"Add frame": "Додати фрейм",
"Frame style": "Тип рамки",
"Frame preset": "Попереднє налаштування кадру",
"Text position": "Положення тексту",
"Scan for more info": "Шукати за додатковою інформацією",
"Text color": "Колір тексту",

View File

@ -34,7 +34,8 @@
"Randomize style": "Randomize style",
"No data!": "No data!",
"Select language": "Select language",
"Select preset": "Select preset",
"Select frame preset": "Select frame preset",
"Select QR code preset": "Select QR code preset",
"GitHub repository for this project": "GitHub repository for this project",
"Data to encode": "Data to encode",
"data to encode e.g. a URL or a string": "data to encode e.g. a URL or a string",
@ -130,6 +131,7 @@
"QR code settings": "QR code settings",
"Add frame": "Add frame",
"Frame style": "Frame style",
"Frame preset": "Frame preset",
"Text position": "Text position",
"Scan for more info": "Scan for more info",
"Text color": "Text color",

View File

@ -34,7 +34,8 @@
"Randomize style": "隨機化風格",
"No data!": "無資料!",
"Select language": "選擇語言",
"Select preset": "選擇預設值",
"Select frame preset": "選擇畫格預設值",
"Select QR code preset": "選擇 QR 代碼預設值",
"GitHub repository for this project": "本專案的 GitHub 套件庫",
"Data to encode": "要編碼的資料",
"data to encode e.g. a URL or a string": "要編碼的資料,例如 URL 或字串",
@ -42,7 +43,7 @@
"Last saved locally": "從本機儲存載入",
"Loaded from file": "從檔案載入",
"Copy QR Code to clipboard": "複製 QR 代碼到剪貼簿",
"Right click the image, and select 'Copy Image'": "Right click the image, and select 'Copy Image'",
"Right click the image, and select 'Copy Image'": "右鍵按一下影像,然後選擇「複製影像",
"Export as": "匯出為",
"Download QR Code as PNG": "下載 QR Code 為 PNG",
"Download QR Code as SVG": "下載 QR Code 為 SVG",
@ -130,6 +131,7 @@
"QR code settings": "QR 代碼設定",
"Add frame": "新增外框",
"Frame style": "外框樣式",
"Frame preset": "框架預設",
"Text position": "文字位置",
"Scan for more info": "掃瞄更多資訊",
"Text color": "文字顏色",

View File

@ -27,6 +27,7 @@ import { parseCSV, validateCSVData } from '@/utils/csv'
import { generateVCardData } from '@/utils/dataEncoding'
import { getNumericCSSValue } from '@/utils/formatting'
import { allPresets, type Preset } from '@/utils/presets'
import { allFramePresets, type FramePreset } from '@/utils/framePresets'
import { useMediaQuery } from '@vueuse/core'
import JSZip from 'jszip'
import {
@ -36,7 +37,7 @@ import {
type ErrorCorrectionLevel,
type Options as StyledQRCodeProps
} from 'qr-code-styling'
import { computed, onMounted, ref, watch } from 'vue'
import { computed, onMounted, ref, watch, nextTick } from 'vue'
import 'vue-i18n'
import { useI18n } from 'vue-i18n'
@ -300,6 +301,44 @@ const frameStyle = ref<FrameStyle>({
borderRadius: '8px',
padding: '16px'
})
const defaultFramePreset = allFramePresets[0]
const selectedFramePresetKey = ref<string>(defaultFramePreset.name)
const lastCustomLoadedFramePreset = ref<FramePreset>()
const CUSTOM_LOADED_FRAME_PRESET_KEYS = [
LAST_LOADED_LOCALLY_PRESET_KEY,
LOADED_FROM_FILE_PRESET_KEY
]
const allFramePresetOptions = computed(() => {
const options = lastCustomLoadedFramePreset.value
? [lastCustomLoadedFramePreset.value, ...allFramePresets]
: allFramePresets
return options.map((preset) => ({ value: preset.name, label: t(preset.name) }))
})
function applyFramePreset(preset: FramePreset) {
if (preset.style) {
frameStyle.value = { ...frameStyle.value, ...preset.style }
}
if (preset.text) frameText.value = preset.text
if (preset.position) frameTextPosition.value = preset.position
showFrame.value = true
}
watch(
selectedFramePresetKey,
(newKey, prevKey) => {
if (newKey === prevKey || !newKey) return
if (CUSTOM_LOADED_FRAME_PRESET_KEYS.includes(newKey) && lastCustomLoadedFramePreset.value) {
applyFramePreset(lastCustomLoadedFramePreset.value)
return
}
const preset = allFramePresets.find((p) => p.name === newKey)
if (preset) {
applyFramePreset(preset)
}
},
{ immediate: true }
)
const frameSettings = computed(() => ({
text: frameText.value,
position: frameTextPosition.value,
@ -481,6 +520,8 @@ function loadQRConfig(jsonString: string, key?: string) {
selectedPresetKey.value = key
}
let framePreset: FramePreset | undefined
selectedPreset.value = preset
if (frameConfig) {
@ -491,6 +532,17 @@ function loadQRConfig(jsonString: string, key?: string) {
...frameStyle.value,
...frameConfig.style
}
framePreset = {
name: key || LAST_LOADED_LOCALLY_PRESET_KEY,
style: frameConfig.style,
text: frameConfig.text,
position: frameConfig.position
}
}
if (framePreset && key) {
lastCustomLoadedFramePreset.value = framePreset
selectedFramePresetKey.value = key
}
}
@ -788,10 +840,10 @@ async function generateBatchQRCodes(format: 'png' | 'svg' | 'jpg') {
resetBatchExportProgress()
}
}
//#endregion
// #endregion
//#region /* Data modal */
const isDataModalVisible = ref(false)
const openDataModal = () => {
isDataModalVisible.value = true
}
@ -804,11 +856,55 @@ const updateDataFromModal = (newData: string) => {
data.value = newData
// Optionally trigger QR code regeneration here if needed
}
// #endregion
//#region /* Dynamic padding for mobile drawer */
const drawerTriggerHeight = ref(0)
const BUFFER_PADDING = 20 // Extra space below the drawer trigger
function updateDrawerTriggerHeight() {
nextTick(() => {
const el = document.getElementById('drawer-preview-container')
if (el) {
drawerTriggerHeight.value = el.offsetHeight
} else {
drawerTriggerHeight.value = 0 // Fallback if element not found
}
})
}
watch(
isLarge,
(newIsLarge) => {
if (!newIsLarge) {
updateDrawerTriggerHeight() // Drawer is now visible
} else {
drawerTriggerHeight.value = 0 // Drawer is hidden, reset padding effect
}
},
{ immediate: true } // Run on initial load
)
// Watch for changes that might affect the drawer trigger's height
watch(showFrame, () => {
if (!isLarge.value) {
updateDrawerTriggerHeight()
}
})
const mainDivPaddingStyle = computed(() => {
if (!isLarge.value && drawerTriggerHeight.value > 0) {
return { paddingBottom: `${drawerTriggerHeight.value + BUFFER_PADDING}px` }
}
return { paddingBottom: '0px' } // Default for large screens or if height is 0
})
//#endregion
</script>
<template>
<div
class="flex items-start justify-center gap-4 pb-[180px] md:flex-row md:gap-6 lg:gap-12 lg:pb-0"
class="flex items-start justify-center gap-4 md:flex-row md:gap-6 lg:gap-12 lg:pb-0"
:style="mainDivPaddingStyle"
>
<!-- Sticky sidebar on large screens -->
<div
@ -1174,7 +1270,7 @@ const updateDataFromModal = (newData: string) => {
<AccordionTrigger
class="button !px-4 text-2xl text-gray-700 outline-none dark:text-gray-100 md:!px-6 lg:!px-8"
><span class="flex flex-row items-center gap-2"
><span>{{ t('Frame settings') }}</span>
><span id="frame-settings-title">{{ t('Frame settings') }}</span>
<span
class="rounded-full bg-white px-2 py-0.5 text-xs font-medium text-zinc-800 dark:bg-zinc-700 dark:text-zinc-200"
>
@ -1183,27 +1279,24 @@ const updateDataFromModal = (newData: string) => {
></AccordionTrigger
>
<AccordionContent class="px-2 pb-8 pt-4">
<div class="space-y-4">
<section class="space-y-4" aria-labelledby="frame-settings-title">
<div class="flex flex-row items-center gap-2">
<label for="show-frame">{{ t('Add frame') }}</label>
<input id="show-frame" type="checkbox" v-model="showFrame" />
</div>
<div v-if="showFrame">
<div class="mb-2 flex flex-row items-center gap-2">
<label for="frame-text">{{ t('Frame text') }}</label>
</div>
<textarea
name="frame-text"
class="text-input"
id="frame-text"
rows="2"
:placeholder="t('Scan for more info')"
v-model="frameText"
<template v-if="showFrame">
<div class="flex flex-col sm:flex-row sm:items-center sm:gap-8">
<div class="flex flex-col sm:w-1/2">
<label>{{ t('Frame preset') }}</label>
<Combobox
:items="allFramePresetOptions"
v-model:value="selectedFramePresetKey"
:button-label="t('Select frame preset')"
/>
</div>
<div v-if="showFrame">
</div>
<div class="flex flex-col">
<label class="mb-2 block">{{ t('Text position') }}</label>
<fieldset class="flex-1" role="radio" tabindex="0">
<div
@ -1222,7 +1315,21 @@ const updateDataFromModal = (newData: string) => {
</fieldset>
</div>
<div v-if="showFrame">
<div>
<div class="mb-2 flex flex-row items-center gap-2">
<label for="frame-text">{{ t('Frame text') }}</label>
</div>
<textarea
name="frame-text"
class="text-input"
id="frame-text"
rows="2"
:placeholder="t('Scan for more info')"
v-model="frameText"
/>
</div>
<div>
<label class="mb-2 block">{{ t('Frame style') }}</label>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
@ -1283,7 +1390,9 @@ const updateDataFromModal = (newData: string) => {
/>
</div>
<div>
<label for="frame-padding" class="mb-1 block text-sm">{{ t('Padding') }}</label>
<label for="frame-padding" class="mb-1 block text-sm">{{
t('Padding')
}}</label>
<input
id="frame-padding"
type="text"
@ -1294,16 +1403,17 @@ const updateDataFromModal = (newData: string) => {
</div>
</div>
</div>
</div>
</template>
</section>
</AccordionContent>
</AccordionItem>
<AccordionItem value="qr-code-settings">
<AccordionTrigger
class="button !px-4 text-2xl text-gray-700 outline-none dark:text-gray-100 md:!px-6 lg:!px-8"
>{{ t('QR code settings') }}</AccordionTrigger
><span id="qr-code-settings-title">{{ t('QR code settings') }}</span></AccordionTrigger
>
<AccordionContent class="px-2 pb-8 pt-4">
<div class="space-y-8">
<section class="space-y-8" aria-labelledby="qr-code-settings-title">
<div>
<label>{{ t('Preset') }}</label>
<div class="flex flex-row items-center justify-start gap-2">
@ -1311,18 +1421,18 @@ const updateDataFromModal = (newData: string) => {
:items="allPresetOptions"
v-model:value="selectedPresetKey"
v-model:open="isPresetSelectOpen"
:button-label="t('Select preset')"
:button-label="t('Select QR code preset')"
:insert-divider-at-indexes="[0, 2]"
/>
<button
class="button"
class="button grid size-10 place-items-center"
@click="randomizeStyleSettings"
:aria-label="t('Randomize style')"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="40"
height="32"
width="24"
height="24"
viewBox="0 0 640 512"
>
<path
@ -1820,7 +1930,7 @@ const updateDataFromModal = (newData: string) => {
</div>
</fieldset>
</div>
</div>
</section>
</AccordionContent>
</AccordionItem>
</Accordion>

57
src/utils/framePresets.ts Normal file
View File

@ -0,0 +1,57 @@
export interface FrameStyle {
textColor: string
backgroundColor: string
borderColor: string
borderWidth: string
borderRadius: string
padding: string
}
export interface FramePreset {
name: string
style: FrameStyle
text?: string
position?: 'top' | 'bottom' | 'left' | 'right'
}
export const defaultFramePreset: FramePreset = {
name: 'Default Frame',
style: {
textColor: '#000000',
backgroundColor: '#ffffff',
borderColor: '#000000',
borderWidth: '1px',
borderRadius: '8px',
padding: '16px'
}
}
export const darkFramePreset: FramePreset = {
name: 'Dark Frame',
style: {
textColor: '#ffffff',
backgroundColor: '#000000',
borderColor: '#ffffff',
borderWidth: '1px',
borderRadius: '8px',
padding: '16px'
}
}
export const borderlessFramePreset: FramePreset = {
name: 'Borderless Frame',
style: {
textColor: '#000000',
backgroundColor: '#ffffff',
borderColor: '#ffffff',
borderWidth: '0px',
borderRadius: '0px',
padding: '16px'
}
}
export const allFramePresets: FramePreset[] = [
defaultFramePreset,
darkFramePreset,
borderlessFramePreset
]

View File

@ -1,24 +0,0 @@
import { test, expect } from '@playwright/test'
test('has title', async ({ page }) => {
await page.goto('/')
// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Mini QR/)
})
test('scans a QR code from file', async ({ page }) => {
await page.goto('/')
// Switch to Scan mode - select the first matching element (likely the desktop one)
await page.getByLabel('Switch to Scan Mode').first().click()
// Locate the hidden file input
const fileInput = page.locator('input[type="file"][accept="image/*"]')
// Set the input file using the fixture
await fileInput.setInputFiles('tests/e2e/fixtures/test-qrcode.png')
// Wait for the result text to appear
await expect(page.getByText('Test QR Data')).toBeVisible()
})

View File

@ -1,305 +1,244 @@
import { test, expect } from '@playwright/test'
import path from 'path'
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
// Recreate __dirname and __filename for ES Modules
// Helper for ES module scope
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
test.beforeEach(async ({ page }) => {
// Go to the page before each test
test.describe('QR Code Creation and Management', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/')
})
// Clear localStorage to prevent state from previous tests
await page.evaluate(() => localStorage.clear())
// Reload the page to apply the cleared storage (optional but safer)
await page.reload()
// Make sure we are in Create mode (though it's the default after reload)
const createModeButton = page.getByLabel('Switch to Create Mode').first()
await expect(createModeButton).toHaveClass(/bg-white/) // Or check another attribute indicating selection
// Clear the data input field to ensure a clean state
const textInput = page.locator('textarea[id="data"]')
await textInput.fill('')
await expect(textInput).toHaveValue('') // Verify it's empty
})
test('creates a QR code', async ({ page }) => {
// Find the text area and fill it.
const textInput = page.locator('textarea[id="data"]')
await textInput.fill('Hello Playwright!')
// Check if the QR code image is visible using its role and name.
const qrCodeImage = page.getByRole('img', { name: 'QR code' })
await expect(qrCodeImage).toBeVisible()
})
test('copy and export buttons are disabled when data is empty', async ({ page }) => {
// Check initial state (data should be empty)
const textInput = page.locator('textarea[id="data"]')
await expect(textInput).toHaveValue('')
// Check buttons are disabled
await expect(page.locator('#copy-qr-image-button')).toBeDisabled()
await expect(page.locator('#download-qr-image-button-png')).toBeDisabled()
await expect(page.locator('#download-qr-image-button-jpg')).toBeDisabled()
await expect(page.locator('#download-qr-image-button-svg')).toBeDisabled()
// Enter some data
await textInput.fill('Test Data')
// Check buttons are now enabled
await expect(page.locator('#copy-qr-image-button')).toBeEnabled()
await expect(page.locator('#download-qr-image-button-png')).toBeEnabled()
await expect(page.locator('#download-qr-image-button-jpg')).toBeEnabled()
await expect(page.locator('#download-qr-image-button-svg')).toBeEnabled()
})
test('save and load QR code config works', async ({ page }) => {
const testData = 'Config Test Data'
const textInput = page.locator('textarea[id="data"]')
const dotsColorInput = page.locator('#dots-color') // Corrected ID
const initialColor = await dotsColorInput.inputValue()
const newColor = '#ff0000'
// Set initial data and change color
await textInput.fill(testData)
await dotsColorInput.fill(newColor)
await expect(dotsColorInput).toHaveValue(newColor)
// Start waiting for the download before clicking the button
const downloadPromise = page.waitForEvent('download')
await page.locator('#save-qr-code-config-button').click()
const download = await downloadPromise
// Ensure the test-results directory exists
const resultsDir = path.resolve(__dirname, '../../test-results')
if (!fs.existsSync(resultsDir)) {
fs.mkdirSync(resultsDir, { recursive: true })
async function openFrameSettings(page: any) {
await page.getByRole('button', { name: /frame settings/i }).click()
}
// Save the downloaded file path
const configPath = path.join(resultsDir, download.suggestedFilename())
await download.saveAs(configPath)
// Clear the input and reset color (or reload the page)
await textInput.fill('')
await dotsColorInput.fill(initialColor) // Reset color to simulate loading
await expect(textInput).toHaveValue('')
await expect(dotsColorInput).toHaveValue(initialColor)
test.describe('Save and Load QR Configs (File Operations)', () => {
const testDataNoFrame = 'Config Test No Frame'
const testDataWithFrame = 'Config Test With Frame'
const frameTextContent = 'My Frame For Config'
const tempDir = path.join(__dirname, 'temp-downloads')
const noFrameConfigFilename = 'qr-config-no-frame.json'
const withFrameConfigFilename = 'qr-config-with-frame.json'
// Locate the load button and set the input file
const loadConfigButton = page.locator('#load-qr-code-config-button')
// Playwright needs a file chooser listener BEFORE the click that triggers it
const fileChooserPromise = page.waitForEvent('filechooser')
await loadConfigButton.click()
const fileChooser = await fileChooserPromise
await fileChooser.setFiles(configPath)
test.beforeAll(async () => {
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true })
}
})
// Verify data and color are restored
await expect(textInput).toHaveValue(testData)
await expect(dotsColorInput).toHaveValue(newColor)
})
test.afterAll(async () => {
if (fs.existsSync(tempDir)) {
fs.rmSync(tempDir, { recursive: true, force: true })
}
})
test('save and load QR code config works with frame', async ({ page }) => {
const testData = 'Frame Config Test'
const frameTextData = 'Scan Me!'
const textInput = page.locator('textarea[id="data"]')
const frameAccordionTrigger = page.getByRole('button', { name: /Frame settings/ })
const showFrameCheckbox = page.locator('input[id="show-frame"]')
const frameTextInput = page.locator('textarea[id="frame-text"]')
const framePositionTopRadio = page.locator('input[id="frameTextPosition-top"]')
// Expand the Frame settings accordion
await frameAccordionTrigger.click()
// Set initial data, enable frame, set text, and change position
await textInput.fill(testData)
await showFrameCheckbox.check()
await frameTextInput.fill(frameTextData)
await framePositionTopRadio.check() // Change from default bottom to top
// Verify initial set state
await expect(showFrameCheckbox).toBeChecked()
await expect(frameTextInput).toHaveValue(frameTextData)
await expect(framePositionTopRadio).toBeChecked()
// Save config
const downloadPromise = page.waitForEvent('download')
await page.locator('#save-qr-code-config-button').click()
const download = await downloadPromise
const configPath = './test-results/' + download.suggestedFilename()
await download.saveAs(configPath)
// Clear inputs and reset frame settings
await textInput.fill('')
test('should save and load QR config without a frame via file', async ({ page }) => {
await page.locator('#data').fill(testDataNoFrame)
await page.locator('#dots-color').fill('#112233') // Change a setting
await openFrameSettings(page)
const showFrameCheckbox = page.locator('#show-frame')
await expect(showFrameCheckbox).toBeVisible()
await expect(showFrameCheckbox).toBeEnabled()
await showFrameCheckbox.uncheck()
// frameTextInput and framePositionTopRadio might be hidden now, no need to explicitly clear/reset
await page.waitForTimeout(600) // Ensure debouncedData updates before saving config
// Verify cleared state (frame checkbox is unchecked)
await expect(textInput).toHaveValue('')
await expect(showFrameCheckbox).not.toBeChecked()
const downloadPromise = page.waitForEvent('download')
await page.locator('#save-qr-code-config-button').click()
const download = await downloadPromise
const downloadedFilePath = path.join(tempDir, noFrameConfigFilename)
await download.saveAs(downloadedFilePath)
expect(fs.existsSync(downloadedFilePath)).toBeTruthy()
// Modify data to ensure loading works
await page.locator('#data').fill('Something else before load')
await page.locator('#dots-color').fill('#000000')
await page.waitForTimeout(600) // Ensure debouncedData updates for the new value
// Load config
const loadConfigButton = page.locator('#load-qr-code-config-button')
const fileChooserPromise = page.waitForEvent('filechooser')
await loadConfigButton.click()
await page.locator('#load-qr-code-config-button').click()
const fileChooser = await fileChooserPromise
await fileChooser.setFiles(configPath)
await fileChooser.setFiles(downloadedFilePath)
await page.waitForTimeout(1000) // Allow time for file processing and reactivity
// Ensure accordion is open again after load if needed (config load might close it)
// Click trigger only if checkbox isn't visible after loading
if (!(await showFrameCheckbox.isVisible())) {
await frameAccordionTrigger.click()
await expect(page.locator('#data')).toHaveValue(testDataNoFrame)
await expect(page.locator('#dots-color')).toHaveValue('#112233')
// Re-open frame settings to check the checkbox state after load
await openFrameSettings(page)
await expect(page.locator('#show-frame')).not.toBeChecked()
})
test('should save and load QR config with a frame via file', async ({ page }) => {
await page.locator('#data').fill(testDataWithFrame)
await openFrameSettings(page)
const showFrameCheckbox = page.locator('#show-frame')
await expect(showFrameCheckbox).toBeVisible()
await expect(showFrameCheckbox).toBeEnabled()
await showFrameCheckbox.check()
await page.locator('#frame-text').fill(frameTextContent)
await page.locator('#frame-text-color').fill('#aabbcc') // Change a frame setting
await page.waitForTimeout(600) // Ensure debouncedData updates before saving config
const downloadPromise = page.waitForEvent('download')
await page.locator('#save-qr-code-config-button').click()
const download = await downloadPromise
const downloadedFilePath = path.join(tempDir, withFrameConfigFilename)
await download.saveAs(downloadedFilePath)
expect(fs.existsSync(downloadedFilePath)).toBeTruthy()
// Modify data
await page.locator('#data').fill('Something different for with frame test') // Make this distinct
await page.waitForTimeout(600) // Ensure debouncedData updates for the new value
await openFrameSettings(page) // Ensure frame settings are open before unchecking
await expect(showFrameCheckbox).toBeVisible()
await expect(showFrameCheckbox).toBeEnabled()
await showFrameCheckbox.uncheck()
const fileChooserPromise = page.waitForEvent('filechooser')
await page.locator('#load-qr-code-config-button').click()
const fileChooser = await fileChooserPromise
await fileChooser.setFiles(downloadedFilePath) // Load the uniquely named file
await page.waitForTimeout(1000) // Allow time for file processing and reactivity
await expect(page.locator('#data')).toHaveValue(testDataWithFrame)
await openFrameSettings(page) // Re-open frame settings to check values
await expect(page.locator('#show-frame')).toBeChecked()
await expect(page.locator('#frame-text')).toHaveValue(frameTextContent)
await expect(page.locator('#frame-text-color')).toHaveValue('#aabbcc')
})
})
test.describe('Export QR Code (Single)', () => {
test('should export as PNG without a frame', async ({ page }) => {
const qrData = 'png-export-no-frame'
await page.locator('#data').fill(qrData)
await openFrameSettings(page)
const showFrameCheckbox = page.locator('#show-frame')
await expect(showFrameCheckbox).toBeVisible()
await expect(showFrameCheckbox).toBeEnabled()
await showFrameCheckbox.uncheck()
await page.waitForTimeout(1000) // Allow QR to re-render and debouncedData to update
await expect(page.locator('#element-to-export')).toBeVisible()
const downloadPromise = page.waitForEvent('download')
await page.locator('#download-qr-image-button-png').click()
const download = await downloadPromise
expect(download.suggestedFilename()).toMatch(/qr-code\.png$/i)
await expect(page.locator('#element-to-export')).toHaveScreenshot('qr-no-frame.png')
})
test('should export as PNG with a frame', async ({ page }) => {
const qrData = 'png-export-with-frame'
const frameText = 'PNG Frame Test'
await page.locator('#data').fill(qrData)
await openFrameSettings(page)
const showFrameCheckbox = page.locator('#show-frame')
await expect(showFrameCheckbox).toBeVisible()
await expect(showFrameCheckbox).toBeEnabled()
await showFrameCheckbox.check()
await page.locator('#frame-text').fill(frameText)
await page.waitForTimeout(1000) // Allow QR to re-render and debouncedData to update
await expect(page.locator('#element-to-export')).toBeVisible()
const downloadPromise = page.waitForEvent('download')
await page.locator('#download-qr-image-button-png').click()
const download = await downloadPromise
expect(download.suggestedFilename()).toMatch(/qr-code\.png$/i)
await expect(page.locator('#element-to-export')).toHaveScreenshot('qr-with-frame.png')
})
test('should export as JPG without a frame', async ({ page }) => {
const qrData = 'jpg-export-no-frame'
await page.locator('#data').fill(qrData)
await openFrameSettings(page)
const showFrameCheckbox = page.locator('#show-frame')
await expect(showFrameCheckbox).toBeVisible()
await expect(showFrameCheckbox).toBeEnabled()
await showFrameCheckbox.uncheck()
await page.waitForTimeout(1000) // Allow QR to re-render and debouncedData to update
await expect(page.locator('#element-to-export')).toBeVisible()
const downloadPromise = page.waitForEvent('download')
await page.locator('#download-qr-image-button-jpg').click()
const download = await downloadPromise
expect(download.suggestedFilename()).toMatch(/qr-code\.jpg$/i)
await expect(page.locator('#element-to-export')).toHaveScreenshot('qr-no-frame-as-jpg.png')
})
test('should export as JPG with a frame', async ({ page }) => {
const qrData = 'jpg-export-with-frame'
const frameText = 'JPG Frame Test'
await page.locator('#data').fill(qrData)
await openFrameSettings(page)
const showFrameCheckbox = page.locator('#show-frame')
await expect(showFrameCheckbox).toBeVisible()
await expect(showFrameCheckbox).toBeEnabled()
await showFrameCheckbox.check()
await page.locator('#frame-text').fill(frameText)
await page.waitForTimeout(1000) // Allow QR to re-render and debouncedData to update
await expect(page.locator('#element-to-export')).toBeVisible()
const downloadPromise = page.waitForEvent('download')
await page.locator('#download-qr-image-button-jpg').click()
const download = await downloadPromise
expect(download.suggestedFilename()).toMatch(/qr-code\.jpg$/i)
await expect(page.locator('#element-to-export')).toHaveScreenshot('qr-with-frame-as-jpg.png')
})
})
test.describe('Batch Export QR Codes', () => {
const simpleCsvPath = '6_strings_batch.csv' // Assuming it's in public folder
test('should batch export QR codes as a zip file of PNGs from CSV input', async ({ page }) => {
// Switch to batch export mode
await page.getByRole('button', { name: /batch export/i }).click()
// Fetch the CSV content (assuming it's served from the public directory)
const csvResponse = await page.request.get('/' + simpleCsvPath)
expect(csvResponse.ok()).toBeTruthy()
const csvContent = await csvResponse.text()
// Prepare for file chooser
const fileChooserPromise = page.waitForEvent('filechooser')
await page.locator('button[aria-label*="Choose a CSV file"]').click()
const fileChooser = await fileChooserPromise
const tempCsvDir = path.join(__dirname, 'temp-csv')
if (!fs.existsSync(tempCsvDir)) {
fs.mkdirSync(tempCsvDir, { recursive: true })
}
const tempCsvFilePath = path.join(tempCsvDir, 'batch.csv')
fs.writeFileSync(tempCsvFilePath, csvContent)
// Verify data and frame settings are restored
await expect(textInput).toHaveValue(testData)
await expect(showFrameCheckbox).toBeChecked()
await expect(frameTextInput).toHaveValue(frameTextData)
await expect(framePositionTopRadio).toBeChecked() // Check if the non-default position was restored
})
await fileChooser.setFiles(tempCsvFilePath)
test('QR code element with frame matches snapshot', async ({ page }) => {
const testData = 'Frame Snapshot Test'
const frameTextData = 'Scan Frame!'
const textInput = page.locator('textarea[id="data"]')
const frameAccordionTrigger = page.getByRole('button', { name: /Frame settings/ })
const showFrameCheckbox = page.locator('input[id="show-frame"]')
const frameTextInput = page.locator('textarea[id="frame-text"]')
await expect(page.locator('text=/\\d+ \\/ \\d+/')).toBeVisible({ timeout: 10000 })
// Expand the Frame settings accordion
await frameAccordionTrigger.click()
// Set data, enable frame, and add frame text
await textInput.fill(testData)
await showFrameCheckbox.check()
await frameTextInput.fill(frameTextData)
// Locate the element containing the QR code and frame
const qrCodeExportElement = page.locator('#element-to-export')
// Ensure the element is visible before taking a snapshot
await expect(qrCodeExportElement).toBeVisible()
// Take a snapshot of the QR code element with the frame
await expect(qrCodeExportElement).toHaveScreenshot('qr-code-with-frame-snapshot.png')
})
test('frame text supports line breaks', async ({ page }) => {
const textInput = page.locator('textarea[id="data"]')
const frameAccordionTrigger = page.getByRole('button', { name: /Frame settings/ })
const showFrameCheckbox = page.locator('input[id="show-frame"]')
const frameTextInput = page.locator('textarea[id="frame-text"]')
// Expand the Frame settings accordion
await frameAccordionTrigger.click()
// Set data, enable frame, and add multiline frame text
await textInput.fill('Multiline Frame Test')
await showFrameCheckbox.check()
const multilineText = 'Line 1\nLine 2\nLine 3'
await frameTextInput.fill(multilineText)
// Locate the paragraph that displays the frame text
const frameTextDisplay = page.locator('#element-to-export p')
// Ensure the displayed text preserves the line breaks
await expect(frameTextDisplay).toHaveText(multilineText)
})
test('QR code element matches snapshot', async ({ page }) => {
const testData = 'Snapshot Test'
const textInput = page.locator('textarea[id="data"]')
await textInput.fill(testData)
// Locate the element containing the QR code to be exported
const qrCodeExportElement = page.locator('#element-to-export')
// Ensure the element is visible before taking a snapshot
await expect(qrCodeExportElement).toBeVisible()
// Take a snapshot of the QR code element
await expect(qrCodeExportElement).toHaveScreenshot('qr-code-snapshot.png')
// We don't need to test the actual download functionality extensively here,
// as the snapshot confirms the visual output. Testing downloads can be flaky.
// But we can check if the buttons exist and are clickable.
await expect(page.locator('#download-qr-image-button-png')).toBeEnabled()
await expect(page.locator('#download-qr-image-button-jpg')).toBeEnabled()
})
test('exported PNG can be scanned', async ({ page }) => {
const testData = 'Export-Scan Test'
const textInput = page.locator('textarea[id="data"]')
await textInput.fill(testData)
// Wait for QR code to render
await expect(page.getByRole('img', { name: 'QR code' })).toBeVisible()
// Start waiting for download and click PNG export button
const downloadPromise = page.waitForEvent('download')
await page.locator('#download-qr-image-button-png').click()
const download = await downloadPromise
// Save to a predictable location for inspection
const exportedPngPath = './exported_qr_code.png'
await download.saveAs(exportedPngPath)
// Switch to Scan mode
await page.getByLabel('Switch to Scan Mode').first().click()
expect(download.suggestedFilename()).toMatch(/qr-codes\.zip$/i)
// Locate the hidden file input for scanning
const scanFileInput = page.locator('input[type="file"][accept="image/*"]')
// Upload the exported PNG
await scanFileInput.setInputFiles(exportedPngPath)
// Wait for the result container to appear first (keep increased timeout for now)
const resultContainer = page.locator('.capture-result')
await expect(resultContainer).toBeVisible({ timeout: 10000 })
// Then verify the content within the container
await expect(resultContainer.getByText(testData)).toBeVisible()
})
test('batch export works with CSV file', async ({ page }) => {
const csvFilePath = 'public/6_strings_batch.csv' // Path to the test CSV
const expectedZipFilename = 'qr-codes.zip'
// Switch to Batch export mode
await page.getByRole('button', { name: 'Batch export' }).click()
// Locate the correct hidden file input for batch CSV upload
const batchFileInput = page.locator('input[type="file"][accept=".csv,.txt"]')
// Upload the CSV file
await batchFileInput.setInputFiles(csvFilePath)
// Check the "Ignore header row" checkbox
const ignoreHeaderCheckbox = page.locator('input[id="ignore-header"]')
await ignoreHeaderCheckbox.check()
await expect(ignoreHeaderCheckbox).toBeChecked()
// Wait for the UI to potentially update after file processing/checkbox click
// Check for the preview text as an indicator
await expect(page.getByText('5 piece(s) of data detected')).toBeVisible() // rows - 1 header
await expect(page.getByText('First row preview:')).toBeVisible()
await expect(page.locator('pre').getByText('https://www.esteetey.dev/')).toBeVisible()
// Start waiting for the download before clicking the PNG export button
const downloadPromise = page.waitForEvent('download')
await page.locator('#download-qr-image-button-png').click()
// Wait for the download to complete
const download = await downloadPromise
// Assert the downloaded file is a zip file with the expected name
expect(download.suggestedFilename()).toBe(expectedZipFilename)
const downloadedPath = './test-results/' + download.suggestedFilename()
await download.saveAs(downloadedPath)
// TODO: Optionally add steps here to unzip and verify file count/contents
// For now, we just check the download happened and was a zip file.
if (fs.existsSync(tempCsvFilePath)) {
fs.unlinkSync(tempCsvFilePath)
}
if (fs.existsSync(tempCsvDir) && fs.readdirSync(tempCsvDir).length === 0) {
fs.rmdirSync(tempCsvDir)
}
})
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

42
tests/e2e/scan.spec.ts Normal file
View File

@ -0,0 +1,42 @@
import { test, expect } from '@playwright/test'
test('scans a QR code from file', async ({ page }) => {
await page.goto('/')
const desktopScanButton = page.locator(
'div.md\\:flex >> button[aria-label*="Switch to Scan Mode"]'
)
const genericScanButton = page.getByLabel(/Switch to Scan Mode/i).first()
if ((await desktopScanButton.count()) > 0 && (await desktopScanButton.isVisible())) {
await desktopScanButton.click()
} else if ((await genericScanButton.count()) > 0 && (await genericScanButton.isVisible())) {
await genericScanButton.click()
} else {
console.error("Could not find a visible 'Switch to Scan Mode' button.")
await page.screenshot({
path: 'test-results/scan-test-scan-button-not-found.png',
fullPage: true
})
throw new Error('Scan mode button not found or not visible.')
}
const fileInput = page.locator('input[type="file"]')
try {
await expect(fileInput).toHaveCount(1, { timeout: 10000 }) // Check it exists
} catch (error) {
console.error(
'QR code file input (\'input[type="file"]\') not found or more than one instance exists after switching to Scan mode.'
)
await page.screenshot({
path: 'test-results/scan-test-qr-file-input-not-found.png',
fullPage: true
})
throw error
}
await fileInput.setInputFiles('tests/e2e/fixtures/test-qrcode.png')
await expect(page.getByText('Test QR Data')).toBeVisible({ timeout: 10000 })
})