Думаю, многие сталкивались с этим: нужно быстро объединить PDF, проверить подсеть или декодировать JWT. Открываешь первый попавшийся сервис, загружаешь файл и надеешься что данные никуда не сохраняются.

Главный принцип простой: если задачу можно выполнить прямо в браузере, зачем вообще нужен сервер?

Так появились два проекта. Каждый - один HTML файл.

Как это работает

Зависимости подгружаются с CDN по требованию - только когда инструмент реально открыт

async function loadLibrary(url) {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = url;
    script.onload = resolve;
    script.onerror = reject;
    document.head.appendChild(script);
  });
}

Это позволяет держать начальный размер маленьким и не грузить то что не нужно. localStorage сохраняет состояние между сессиями автоматически

input.addEventListener('input', () => {
  localStorage.setItem(`tool_${toolId}_input`, input.value);
});
input.value = localStorage.getItem(`tool_${toolId}_input`) || '';

DarkenAmber IT Tools

17 инструментов для сети и разработки. 194KB, ноль зависимостей в начальной загрузке.

Хэши через Web Crypto API без внешних библиотек

const buffer = await crypto.subtle.digest(
  'SHA-256',
  new TextEncoder().encode(input)
);
const hash = Array.from(new Uint8Array(buffer))
  .map(b => b.toString(16).padStart(2, '0'))
  .join('');

JWT декодируется без библиотек - base64url отличается от стандартного base64

const [header, payload] = token.split('.')
  .slice(0, 2)
  .map(part => JSON.parse(atob(
    part.replace(/-/g, '+').replace(/_/g, '/')
  )));

IP калькулятор считает через побитовые операции

function calculateSubnet(ip, prefix) {
  const mask = ~(0xFFFFFFFF >>> prefix) >>> 0;
  const network = (ipToInt(ip) & mask) >>> 0;
  const broadcast = (network | ~mask) >>> 0;
  return {
    network: intToIp(network),
    broadcast: intToIp(broadcast),
    hosts: Math.pow(2, 32 - prefix) - 2
  };
}

GitHub: https://github.com/DarkenAmber/DarkenAmber-it-tools

Live: https://darkenamber.github.io/DarkenAmber-it-tools

ZeroOffice

PDF, изображения, текст, графики, AI ассистент. 480KB, работает офлайн.

PDF манипуляции через pdf-lib

async function mergePdfs(files) {
  const merged = await PDFLib.PDFDocument.create();
  for (const file of files) {
    const bytes = await file.arrayBuffer();
    const doc = await PDFLib.PDFDocument.load(bytes);
    const pages = await merged.copyPages(doc, doc.getPageIndices());
    pages.forEach(p => merged.addPage(p));
  }
  return merged.save();
}

Рендеринг PDF для предпросмотра через PDF.js

const page = await pdf.getPage(1);
const viewport = page.getViewport({ scale: 1.5 });
await page.render({
  canvasContext: canvas.getContext('2d'),
  viewport
}).promise;

Chart Builder строит графики через Chart.js и экспортирует в PNG, SVG и Excel через ExcelJS - всё локально.

AI инструменты работают через Claude API напрямую из браузера - ZeroOffice не посередине:

const response = await fetch('https://api.anthropic.com/v1/messages', {
  headers: {
    'x-api-key': localStorage.getItem('claude_api_key'),
    'anthropic-version': '2023-06-01',
  },
  body: JSON.stringify({ model: 'claude-3-haiku-20240307', ... })
});

GitHub: https://github.com/DarkenAmber/ZeroOffice

Live: https://darkenamber.github.io/ZeroOffice

Практика показала что большинство повседневных задач с файлами и данными можно решить без сервера. Браузер получил достаточно API - Web Crypto, Canvas, File System Access, Web Workers - чтобы делать полноценные инструменты локально.

Оба проекта ещё сырые и дорабатываются. В планах новые инструменты, локализация и улучшение мобильной версии. Буду рад замечаниям и идеям в комментариях.


Комментарии (0)