From 2638f6638ca682b3ebf0722eb19191a92f7efa0f Mon Sep 17 00:00:00 2001 From: DocFast Bot Date: Sat, 14 Feb 2026 15:02:02 +0000 Subject: [PATCH 1/2] fix(BUG-004): extract inline JS to app.js for CSP compliance --- public/app.js | 83 +++++++++++++++++++++++++++++++++++++++++++++ public/index.html | 86 +---------------------------------------------- 2 files changed, 84 insertions(+), 85 deletions(-) create mode 100644 public/app.js diff --git a/public/app.js b/public/app.js new file mode 100644 index 0000000..886d72f --- /dev/null +++ b/public/app.js @@ -0,0 +1,83 @@ +function openSignup() { + document.getElementById('signupModal').classList.add('active'); + document.getElementById('signupForm').style.display = 'block'; + document.getElementById('keyResult').style.display = 'none'; + document.getElementById('signupEmail').value = ''; + document.getElementById('signupError').style.display = 'none'; + setTimeout(function() { document.getElementById('signupEmail').focus(); }, 100); +} + +function closeSignup() { + document.getElementById('signupModal').classList.remove('active'); +} + +// Close on overlay click +document.getElementById('signupModal').addEventListener('click', function(e) { + if (e.target === this) closeSignup(); +}); + +// Submit on Enter +document.getElementById('signupEmail').addEventListener('keydown', function(e) { + if (e.key === 'Enter') submitSignup(); +}); + +async function submitSignup() { + var email = document.getElementById('signupEmail').value.trim(); + var errEl = document.getElementById('signupError'); + var btn = document.getElementById('signupBtn'); + + if (!email) { + errEl.textContent = 'Please enter your email.'; + errEl.style.display = 'block'; + return; + } + + btn.textContent = 'Creating...'; + btn.disabled = true; + + try { + var res = await fetch('/v1/signup/free', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: email }) + }); + var data = await res.json(); + + if (!res.ok) { + errEl.textContent = data.error || 'Something went wrong.'; + errEl.style.display = 'block'; + btn.textContent = 'Get API Key'; + btn.disabled = false; + return; + } + + // Show key + document.getElementById('signupForm').style.display = 'none'; + document.getElementById('keyResult').style.display = 'block'; + document.getElementById('apiKeyDisplay').textContent = data.apiKey; + } catch (err) { + errEl.textContent = 'Network error. Please try again.'; + errEl.style.display = 'block'; + btn.textContent = 'Get API Key'; + btn.disabled = false; + } +} + +function copyKey() { + var key = document.getElementById('apiKeyDisplay').textContent; + navigator.clipboard.writeText(key).then(function() { + document.querySelector('.copy-hint').textContent = '✓ Copied!'; + setTimeout(function() { document.querySelector('.copy-hint').textContent = 'Click to copy'; }, 2000); + }); +} + +async function checkout() { + try { + var res = await fetch('/v1/billing/checkout', { method: 'POST' }); + var data = await res.json(); + if (data.url) window.location.href = data.url; + else alert('Something went wrong. Please try again.'); + } catch (err) { + alert('Something went wrong. Please try again.'); + } +} diff --git a/public/index.html b/public/index.html index b7fd8fc..8dbfadc 100644 --- a/public/index.html +++ b/public/index.html @@ -238,90 +238,6 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0. - + From 6891e488b629b4bc4e5766135e5336c3219a8b4e Mon Sep 17 00:00:00 2001 From: DocFast Bot Date: Sat, 14 Feb 2026 15:27:45 +0000 Subject: [PATCH 2/2] fix(BUG-006,007,008): copy feedback, invoice data unwrap, zero margins BUG-007: Unwrap req.body.data for template endpoint (docs show wrapped format) BUG-008: Default PDF margins set to 0 (user can override via request body) BUG-006: Copy button shows Copied! for 2s then reverts --- public/app.js | 14 ++++++++++++-- public/index.html | 12 ++++++------ src/routes/templates.ts | 2 +- src/services/browser.ts | 16 ++++++++-------- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/public/app.js b/public/app.js index 886d72f..4040a43 100644 --- a/public/app.js +++ b/public/app.js @@ -66,8 +66,8 @@ async function submitSignup() { function copyKey() { var key = document.getElementById('apiKeyDisplay').textContent; navigator.clipboard.writeText(key).then(function() { - document.querySelector('.copy-hint').textContent = '✓ Copied!'; - setTimeout(function() { document.querySelector('.copy-hint').textContent = 'Click to copy'; }, 2000); + var btn = document.getElementById('apiKeyDisplay'); var origText = btn.textContent; btn.textContent = 'Copied!'; document.querySelector('.copy-hint').textContent = '✓ Copied!'; + setTimeout(function() { btn.textContent = origText; document.querySelector('.copy-hint').textContent = 'Click to copy'; }); } @@ -81,3 +81,13 @@ async function checkout() { alert('Something went wrong. Please try again.'); } } + +// BUG-005 fix: attach all click handlers via JS instead of inline onclick +document.addEventListener('DOMContentLoaded', function() { + document.getElementById('btn-signup').addEventListener('click', openSignup); + document.getElementById('btn-signup-2').addEventListener('click', openSignup); + document.getElementById('btn-checkout').addEventListener('click', checkout); + document.getElementById('btn-close-signup').addEventListener('click', closeSignup); + document.getElementById('signupBtn').addEventListener('click', submitSignup); + document.getElementById('apiKeyDisplay').addEventListener('click', copyKey); +}); diff --git a/public/index.html b/public/index.html index 8dbfadc..b93fb9a 100644 --- a/public/index.html +++ b/public/index.html @@ -91,7 +91,7 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0.

HTML & Markdown to PDF

One API call. Beautiful PDFs. Built-in invoice templates. No headless browser setup, no dependencies, no hassle.

- + View Docs
@@ -193,7 +193,7 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0.
  • All templates
  • Community support
  • - +
    @@ -220,18 +220,18 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0.