Top Banner
🚀 Transcribe Unlimited Audio & Video! Save 60% Today
Freelance Toolkit

for freelancers who’d rather build than write from scratch

Freelance Toolkit

25 tools for the parts of freelancing that aren’t the actual work — profiles, proposals, pricing, and the emails you keep putting off. Fill in a few fields, get a draft, edit to taste.

drafts to start from, not final answers — read everything before you send it
${toolsInCat.map(buildToolCard).join('')}
`; });navGroupsEl.innerHTML = navHtml; mainContentEl.innerHTML = mainHtml;wireGenerateButtons(); wireCopyButtons(); setupActiveNavObserver(); }/* ---------- Filter ---------- */ filterInput.addEventListener('input', () => { const q = filterInput.value.trim().toLowerCase(); document.querySelectorAll('.nav-group').forEach(group => { let anyVisible = false; group.querySelectorAll('li').forEach(li => { const match = li.textContent.toLowerCase().includes(q); li.style.display = match ? '' : 'none'; if(match) anyVisible = true; }); group.style.display = anyVisible ? '' : 'none'; }); });/* ---------- Markdown-lite renderer ---------- */ function mdToHtml(md){ const lines = md.split('\n'); let html = ''; let listType = null; let paragraphBuffer = [];function inline(text){ return escapeHtml(text) .replace(/\*\*(.+?)\*\*/g, '$1') .replace(/(^|[^*])\*([^*]+)\*($|[^*])/g, '$1$2$3'); } function flushParagraph(){ if(paragraphBuffer.length){ html += '

' + inline(paragraphBuffer.join(' ')) + '

'; paragraphBuffer = []; } } function closeList(){ if(listType){ html += ``; listType = null; } }lines.forEach(rawLine => { const line = rawLine.trim(); if(line === ''){ flushParagraph(); closeList(); return; } if(/^###\s+/.test(line)){ flushParagraph(); closeList(); html += `

${inline(line.replace(/^###\s+/,''))}

`; return; } if(/^##\s+/.test(line)){ flushParagraph(); closeList(); html += `

${inline(line.replace(/^##\s+/,''))}

`; return; } if(/^[-*]\s+/.test(line)){ flushParagraph(); if(listType !== 'ul'){ closeList(); html += '
    '; listType = 'ul'; } html += `
  • ${inline(line.replace(/^[-*]\s+/,''))}
  • `; return; } if(/^\d+\.\s+/.test(line)){ flushParagraph(); if(listType !== 'ol'){ closeList(); html += '
      '; listType = 'ol'; } html += `
    1. ${inline(line.replace(/^\d+\.\s+/,''))}
    2. `; return; } closeList(); paragraphBuffer.push(line); }); flushParagraph(); closeList(); return html; }/* ---------- Generate ---------- */ function wireGenerateButtons(){ document.querySelectorAll('.generate-btn').forEach(btn => { btn.addEventListener('click', () => { const tool = TOOLS.find(t => t.id === btn.dataset.tool); if(tool) generateForTool(tool); }); }); }async function generateForTool(tool){ const section = document.getElementById(tool.id); const button = section.querySelector('.generate-btn'); const errorBox = section.querySelector('.error-box'); const resultWrap = section.querySelector('.result-wrap'); const resultContent = section.querySelector('.result-content');let valid = true; const values = {}; tool.fields.forEach(f => { const el = document.getElementById(`${tool.id}__${f.key}`); const val = el.value.trim(); values[f.key] = val; const isRequired = f.required !== false; if(isRequired && !val){ el.classList.add('error'); valid = false; } else { el.classList.remove('error'); } }); if(!valid) return;button.disabled = true; button.classList.add('loading'); errorBox.hidden = true;const userLines = tool.fields .filter(f => values[f.key]) .map(f => `${f.label}: ${values[f.key]}`) .join('\n'); const userPrompt = `${userLines}\n\nGenerate the requested output now, following every rule exactly.`;try{ const response = await fetch("https://api.anthropic.com/v1/messages", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model: "claude-sonnet-4-6", max_tokens: 1000, system: tool.systemPrompt + '\n\n' + FORMAT_INSTRUCTION, messages: [{ role: "user", content: userPrompt }] }) });if(!response.ok) throw new Error("Request failed: " + response.status);const data = await response.json(); const text = (data.content || []).map(b => b.text || '').join('').trim(); if(!text) throw new Error("Empty response");resultContent.innerHTML = mdToHtml(text); resultWrap.hidden = false; resultWrap.scrollIntoView({ behavior: 'smooth', block: 'nearest' });} catch(err){ console.error(err); errorBox.hidden = false; } finally { button.disabled = false; button.classList.remove('loading'); } }/* ---------- Copy ---------- */ function wireCopyButtons(){ document.querySelectorAll('.copy-btn').forEach(btn => { btn.addEventListener('click', async () => { const content = btn.closest('.result-wrap').querySelector('.result-content'); const text = content.innerText; try{ await navigator.clipboard.writeText(text); } catch(e){ const ta = document.createElement('textarea'); ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); } const original = btn.textContent; btn.textContent = 'copied'; btn.classList.add('copied'); setTimeout(() => { btn.textContent = original; btn.classList.remove('copied'); }, 1400); }); }); }/* ---------- Active nav highlighting ---------- */ function setupActiveNavObserver(){ const links = document.querySelectorAll('[data-tool-link]'); const linkMap = {}; links.forEach(l => linkMap[l.dataset.toolLink] = l);const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { const link = linkMap[entry.target.id]; if(!link) return; if(entry.isIntersecting){ links.forEach(l => l.classList.remove('active')); link.classList.add('active'); } }); }, { rootMargin: '-15% 0px -70% 0px', threshold: 0 });TOOLS.forEach(t => { const el = document.getElementById(t.id); if(el) observer.observe(el); }); }renderPage();
Scroll to Top