Compare commits

...
Sign in to create a new pull request.

13 commits

Author SHA1 Message Date
d40125680a Update banner description
All checks were successful
Deploy / deploy (push) Successful in 37s
2025-07-30 09:51:22 -03:00
5da7f7e58f Merge pull request 'Add google ads' (#33) from ads into main
All checks were successful
Deploy / deploy (push) Successful in 25s
Reviewed-on: #33
2025-07-24 19:41:23 +00:00
9c971ff335 Add google ads 2025-07-24 16:40:55 -03:00
d5d90fb3be Update email input label
All checks were successful
Deploy / deploy (push) Successful in 29s
2025-07-15 10:05:37 -03:00
33623e182b Update logo for README
All checks were successful
Deploy / deploy (push) Successful in 27s
2025-07-14 19:24:29 -03:00
a8b6559682 Add logo to README
All checks were successful
Deploy / deploy (push) Successful in 32s
2025-07-14 19:14:02 -03:00
0d34338f84 Update email key
All checks were successful
Deploy / deploy (push) Successful in 22s
2025-07-13 17:32:16 -03:00
e58f8147e1 Update email field name
All checks were successful
Deploy / deploy (push) Successful in 22s
2025-07-13 16:58:20 -03:00
efe4ba9e98 Merge pull request 'Add banner to ask users to be a beta tester' (#32) from calling-testers into main
All checks were successful
Deploy / deploy (push) Successful in 30s
Reviewed-on: #32
2025-07-13 19:53:46 +00:00
dd6f90a622 Localize banner 2025-07-13 16:51:02 -03:00
76bf1dacee Remove form header 2025-07-13 14:24:35 -03:00
050ecc26ca Add banner and update env variables 2025-07-13 14:24:06 -03:00
7f771b10fa Merge pull request 'development' (#31) from development into main
All checks were successful
Deploy / deploy (push) Successful in 22s
Reviewed-on: #31
2025-07-11 13:59:11 +00:00
20 changed files with 292 additions and 16 deletions

View file

@ -20,8 +20,17 @@ jobs:
chmod 600 ./deploy.key
echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
- name: Create env file
run: |
touch .env
echo EMAIL_ACCESS_KEY=${{ secrets.EMAIL_ACCESS_KEY }} >> .env
echo EMAIL_BASE_URL=${{ secrets.EMAIL_BASE_URL }} >> .env
- name: Verify .env file creation
run: cat .env
- name: Install PM2
run: npm i -g pm2
- name: Deploy
run: pm2 deploy ecosystem.config.cjs production
run: env $(cat .env | grep -v \"#\" | xargs) pm2 deploy ecosystem.config.cjs production

View file

@ -1,5 +1,7 @@
# Embroidery Viewer
![Logo](/logo.webp)
![Deploy workflow status](https://git.leomurca.xyz/leomurca/embroidery-viewer/actions/workflows/deploy.yml/badge.svg)
A free online tool to view embroidery files.
@ -9,4 +11,4 @@ Available at https://embroideryviewer.xyz.
Current supported formats: **.pes, .dst, .pec, .jef and .exp**.
Inspired by https://github.com/redteam316/html5-embroidery.git..
Inspired by https://github.com/redteam316/html5-embroidery.git.

View file

@ -10,6 +10,8 @@ module.exports = {
watch: false,
max_memory_restart: '1G',
env: {
EMAIL_ACCESS_KEY: process.env.EMAIL_ACCESS_KEY,
EMAIL_BASE_URL: process.env.EMAIL_BASE_URL,
NODE_ENV: 'production',
PORT: 7281,
},
@ -27,6 +29,8 @@ module.exports = {
'post-deploy':
'npm run build && pm2 reload ecosystem.config.cjs --only embroidery-viewer-prod --env production && pm2 save',
env: {
EMAIL_ACCESS_KEY: process.env.EMAIL_ACCESS_KEY,
EMAIL_BASE_URL: process.env.EMAIL_BASE_URL,
PORT: 7281,
NODE_ENV: 'production',
},

BIN
logo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -1,7 +1,7 @@
{
"name": "embroidery-viewer",
"private": true,
"version": "2.1.1",
"version": "2.1.3",
"type": "module",
"scripts": {
"dev": "vite dev",

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View file

@ -18,5 +18,14 @@
"seo.description": "Upload and preview embroidery files instantly with Embroidery Viewer. Supports DST, PES, JEF, EXP, VP3 and more. No installs, no uploads 100% browser-based and free.",
"seo.keywords": "embroidery viewer, online embroidery viewer, embroidery file preview, DST viewer, PES viewer, free embroidery tool, JEF viewer, EXP embroidery, VP3 embroidery viewer, embroidery preview tool, browser embroidery renderer, convert embroidery to PNG",
"seo.url": "https://embroideryviewer.xyz",
"seo.image": "https://embroideryviewer.xyz/og/"
"seo.image": "https://embroideryviewer.xyz/og/",
"banner.title": "🚀 Be a Beta Tester",
"banner.subtitle": "Were launching the <strong style=\"color: white\">Embroidery Viewer Android App </strong> — and you can be one of the first to try it!",
"banner.description": "Enjoy a smooth, ad-free experience to visualize PES, JEF, PEC, VP3, DST and EXP embroidery files right from your phone. Help us improve it and get early access before everyone else.",
"banner.name": "Name",
"banner.email": "Google Account Email (used for accessing the Play Store)",
"banner.cta": "Join the Beta",
"banner.cta.loading": "Joining...",
"banner.feedback.success": "Name and email sent successfully! We'll contact you soon!",
"banner.feedback.error": "Something went wrong. Try sending you name and e-mail directly to leo@leomurca.xyz."
}

View file

@ -18,5 +18,14 @@
"seo.description": "Envie e visualize arquivos de bordado instantaneamente com o Embroidery Viewer. Compatível com DST, PES, JEF, EXP, VP3 e mais. Sem instalações, sem uploads 100% no navegador e gratuito.",
"seo.keywords": "visualizador de bordado, visualizador online de bordado, visualizar arquivos de bordado, visualizar DST, visualizar PES, ferramenta gratuita de bordado, visualizador JEF, bordado EXP, visualizador VP3, pré-visualização de bordado, renderizador de bordado no navegador, converter bordado em PNG",
"seo.url": "https://embroideryviewer.xyz",
"seo.image": "https://embroideryviewer.xyz/og/"
"seo.image": "https://embroideryviewer.xyz/og/",
"banner.title": "🚀 Seja um Testador Beta",
"banner.subtitle": "Estamos lançando o aplicativo <strong style=\"color: white\">Embroidery Viewer para Android</strong> — e você pode ser um dos primeiros a experimentá-lo!",
"banner.description": "Desfrute de uma experiência fluida e sem anúncios para visualizar arquivos de bordado PES, JEF, PEC, VP3, DST e EXP diretamente do seu celular. Ajude-nos a melhorá-lo e obtenha acesso antecipado antes de todo mundo.",
"banner.name": "Nome",
"banner.email": "E-mail da sua conta Google (usada na Play Store)",
"banner.cta": "Seja um Testador",
"banner.cta.loading": "Enviando...",
"banner.feedback.success": "Nome e e-mail enviados com sucesso! Entraremos em contato em breve!",
"banner.feedback.error": "Algo deu errado. Tente enviar seu nome e e-mail diretamente para leo@leomurca.xyz."
}

View file

@ -28,7 +28,7 @@
<style>
main {
flex: 1; /* This pushes footer to bottom */
padding: 20px;
padding: 0;
min-height: 90vh;
}
</style>

View file

@ -1,3 +1,4 @@
/** @type {import('./$types').PageLoad} */
export function load() {
return {

View file

@ -0,0 +1,34 @@
import { EMAIL_ACCESS_KEY, EMAIL_BASE_URL } from '$env/static/private';
/** @type {import('./$types').Actions} */
export const actions = {
default: async ({ request }) => {
const formData = await request.formData();
console.log(formData);
const response = await fetch(`${EMAIL_BASE_URL}/submit`, {
method: 'POST',
body: JSON.stringify({
accessKey: EMAIL_ACCESS_KEY,
subject: 'Contato - Embroidery Viewer Beta Testers!',
name: formData.get('name'),
email: formData.get('email'),
}),
headers: { 'Content-Type': 'application/json' }
});
const json = await response.json();
if (json.error === undefined) {
return {
message: "home.banner.feedback.success",
textColor: 'green'
};
} else {
return {
message: 'home.banner.feedback.error',
textColor: 'red'
};
}
}
};

View file

@ -1,15 +1,84 @@
<script>
import Seo from '$lib/components/Seo.svelte';
import { t } from '$lib/translations';
// @ts-nocheck
import { applyAction, enhance } from '$app/forms';
import { invalidateAll } from '$app/navigation';
import { t, locale, SUPPORTED_LOCALES } from '$lib/translations';
import Seo from '$lib/components/Seo.svelte';
import appScreenshot from '$lib/assets/app-with-frame.png';
import appScreenshotPt from '$lib/assets/app-with-frame-pt.png';
import { browser } from '$app/environment';
/** @type {import('./$types').PageProps} */
let { data } = $props();
/**
* @type {{ textColor: String; message: String; } | null}
*/
let feedbackMessage = $state(null);
/**
* @type {boolean}
*/
let loading = $state(false);
const metadata = data.metadata;
const resetFeedback = () => {
if (feedbackMessage) feedbackMessage = null
}
</script>
<Seo {...metadata} />
<div class="beta-section">
<div class="beta-content">
<div class="beta-image">
<img src={$locale === SUPPORTED_LOCALES.EN_US ? appScreenshot : appScreenshotPt} alt="Embroidery Viewer App Screenshot" />
</div>
<div class="beta-text">
<h1>{$t("home.banner.title")}</h1>
<p class="lead">
{@html $t('home.banner.subtitle')}
</p>
<p>
{$t('home.banner.description')}
</p>
<form
action="/"
method="POST"
class="beta-form"
use:enhance={() => {
loading = true;
return async ({ result }) => {
loading = false;
feedbackMessage = result.data;
if (result.type === 'success') await invalidateAll();
applyAction(result);
};
}}>
<label for="name">{$t("home.banner.name")}</label>
<input type="text" name="name" id="name" oninput={resetFeedback} required />
<label for="email">{$t("home.banner.email")}</label>
<input type="email" name="email" id="email" oninput={resetFeedback} required />
<button type="submit">{$t(loading ? 'home.banner.cta.loading' : 'home.banner.cta') }</button>
{#if feedbackMessage !== null}
<p style="margin-top: 1rem; color: {feedbackMessage.textColor}">
{$t(feedbackMessage.message)}
</p>
{/if}
</form>
</div>
</div>
</div>
<div class="home-container">
<section aria-labelledby="main-title">
<h1 id="main-title">{$t('home.main.title')}</h1>
@ -51,14 +120,152 @@
</div>
<style>
.beta-form {
background: white;
color: #000;
padding: 1.5rem;
border-radius: 10px;
max-width: 400px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
}
.beta-form label {
display: block;
margin-top: 1rem;
font-weight: bold;
}
.beta-form input {
width: 100%;
padding: 0.6rem;
margin-top: 0.3rem;
border: 1px solid #ccc;
border-radius: 6px;
}
.beta-form button {
margin-top: 1.5rem;
width: 100%;
background-color: #05345f;
color: white;
border: none;
padding: 0.8rem;
font-size: 1rem;
border-radius: 6px;
cursor: pointer;
}
.beta-form button:disabled {
background-color: #7aa3c1;
cursor: wait;
}
.beta-form button:hover:enabled {
background-color: #042b4f;
}
.home-container {
margin: 0 auto;
width: 70%;
padding-top: 20px;
}
@media (max-width: 768px) {
.home-container {
width: 90%;
}
}
.beta-section {
background-color: #05345f;
color: white;
padding: 3rem 1rem;
display: flex;
justify-content: center;
}
.beta-content {
display: flex;
flex-wrap: wrap;
gap: 2rem;
max-width: 1000px;
align-items: center;
width: 100%;
}
.beta-image img {
max-width: 300px;
border-radius: 1rem;
}
.beta-text {
flex: 1;
min-width: 280px;
}
.beta-text h1 {
font-size: 2rem;
margin-bottom: 0.5rem;
color: white;
}
.beta-text .lead {
font-size: 1.2rem;
margin-bottom: 1rem;
font-weight: 500;
}
.beta-text p {
margin-bottom: 1rem;
line-height: 1.5;
}
.beta-form {
background: white;
color: #000;
padding: 1.5rem;
border-radius: 10px;
max-width: 400px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
}
.beta-form label {
display: block;
margin-top: 1rem;
font-weight: bold;
}
.beta-form input {
width: 100%;
padding: 0.6rem;
margin-top: 0.3rem;
border: 1px solid #ccc;
border-radius: 6px;
}
.beta-form button {
margin-top: 1.5rem;
width: 100%;
background-color: #05345f;
color: white;
border: none;
padding: 0.8rem;
font-size: 1rem;
border-radius: 6px;
cursor: pointer;
}
.beta-form button:hover {
background-color: #042b4f;
}
@media (max-width: 768px) {
.beta-content {
flex-direction: column;
align-items: center;
}
.beta-form {
margin: 0 auto;
}
}
</style>

View file

@ -30,7 +30,7 @@
@media (max-width: 768px) {
section {
width: 100%;
width: 90%;
}
}
</style>

View file

@ -164,7 +164,7 @@
@media (max-width: 768px) {
.donate-container {
width: 100%;
width: 90%;
}
.donation-options {

View file

@ -28,7 +28,7 @@
@media (max-width: 768px) {
section {
width: 100%;
width: 90%;
}
}
</style>

View file

@ -28,7 +28,7 @@
@media (max-width: 768px) {
section {
width: 100%;
width: 90%;
}
}
</style>

View file

@ -28,7 +28,7 @@
@media (max-width: 768px) {
section {
width: 100%;
width: 90%;
}
}
</style>

View file

@ -147,7 +147,7 @@
@media only screen and (max-device-width: 768px) {
#form {
width: 100%;
width: 90%;
}
}
</style>

1
static/ads.txt Normal file
View file

@ -0,0 +1 @@
google.com, pub-5761689301112420, DIRECT, f08c47fec0942fa0