Ctrl K
ComunidadGuías…

frontend/index.html

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="color-scheme" content="dark light" />
    <!-- Preload the faces painted above the fold: Manrope carries the body,
         Bricolage the headline. -->
    <link rel="preload" as="font" type="font/woff2" crossorigin
          href="/fonts/skins/manrope-400-73389ca9.ce093b341d.woff2" />
    <link rel="preload" as="font" type="font/woff2" crossorigin
          href="/fonts/bricolage-grotesque-latin-700-normal.woff2" />
    <title>PrivaTools — Free, Open-Source Privacy-First File Tools</title>
    <meta name="description" content="Free, open-source file tools — PDF, image, video, and developer utilities. Browser-only when possible; isolated, self-hostable processing when needed. No account required for tools.">
    <meta name="author" content="PrivaTools" />
    <meta name="theme-color" content="#151619" media="(prefers-color-scheme: dark)" />
    <meta name="theme-color" content="#edf3f9" media="(prefers-color-scheme: light)" />
    <meta name="mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-title" content="PrivaTools" />
    <meta name="apple-mobile-web-app-status-bar-style" content="default" />
    <meta name="robots" content="index,follow,max-image-preview:large" />
    <meta property="og:title" content="PrivaTools — Free, Open-Source Privacy-First File Tools">
    <meta property="og:description" content="Free, open-source file tools — PDF, image, video, and developer utilities. Browser-only when possible; isolated backend processing when needed.">
    <meta property="og:type" content="website" />
    <meta property="og:url" content="https://privatools.me" />
    <meta property="og:site_name" content="PrivaTools" />
    <meta property="og:image" content="https://privatools.me/og-image.png" />
    <meta property="og:image:width" content="1200" />
    <meta property="og:image:height" content="630" />
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:title" content="PrivaTools — Free, Open-Source Privacy-First File Tools">
    <meta name="twitter:description" content="Free, open-source file tools. Browser-only when possible; isolated backend processing when needed.">
    <meta name="twitter:image" content="https://privatools.me/og-image.png">
    <link rel="icon" type="image/png" sizes="96x96" href="/brand/privatools-icon-96.png" />
    <link rel="icon" type="image/svg+xml" href="/brand/privatools-favicon.svg" />
    <link rel="alternate" type="application/rss+xml" title="PrivaTools Journal" href="/feed.xml" />
    <link rel="manifest" href="/manifest.json" />
    <link rel="search" type="application/opensearchdescription+xml" title="PrivaTools" href="/opensearch.xml" />
    <link rel="apple-touch-icon" sizes="180x180" href="/brand/privatools-icon-180.png" />
    <style>
      :root { --prepaint-bg:#edf3f9; --prepaint-ink:#23394f; --prepaint-accent:#285cc7; --prepaint-line:#ccd9e7; }
      html[data-experience="play"] { --prepaint-bg:#fae7eb; --prepaint-ink:#472732; --prepaint-accent:#a32749; --prepaint-line:#c9a4ae; }
      html[data-theme="dark"] { --prepaint-bg:#151619; --prepaint-ink:#f3f4f7; --prepaint-accent:#91b5ff; --prepaint-line:#383d46; }
      html[data-experience="play"][data-theme="dark"] { --prepaint-bg:#181818; --prepaint-ink:#f7f4ef; --prepaint-accent:#ff9677; --prepaint-line:#42413f; }
      html { background:var(--prepaint-bg); }
      #prepaint-brand {
        position: fixed;
        z-index: 95;
        top: 18px;
        left: 16px;
        pointer-events: none;
        font-family: "Manrope", system-ui, sans-serif;
        font-size: 20px;
        line-height: 1;
        font-weight: 700;
        letter-spacing: -0.03em;
        color: var(--prepaint-ink);
      }
      html[data-experience="play"] #prepaint-brand { font-family:"Bricolage Grotesque", "Manrope", system-ui, sans-serif; }
      #prepaint-brand em { font-style: normal; color: var(--prepaint-accent); }
      /* The prepaint brand exists only to paint something before hydration.
         SkinAppHost sets .app-ready on mount and it retires. */
      .app-ready #prepaint-brand { display: none; }
      #prepaint-shell { background:var(--prepaint-bg); color:var(--prepaint-ink); }
      /* The skip link is styled here, not with utility classes: Tailwind does
         not scan this file, and the link has to work before the app loads.
         Hidden until focused, then a solid action-colour tab over the header. */
      #prepaint-skip {
        position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
        overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
      }
      #prepaint-skip:focus {
        position: fixed; z-index: 1000; top: 12px; left: 12px;
        width: auto; height: auto; margin: 0; padding: 12px 18px;
        overflow: visible; clip: auto; clip-path: none;
        border-radius: 10px; background: var(--prepaint-accent); color: var(--prepaint-bg);
        font: 650 15px/1.2 "Manrope", system-ui, sans-serif; text-decoration: none;
        outline: 2px solid var(--prepaint-ink); outline-offset: 3px;
        box-shadow: 0 10px 28px rgba(0, 0, 0, .2);
      }
      html[data-experience="play"] #prepaint-skip:focus { border-radius: 999px; font-family: "Outfit", "Manrope", system-ui, sans-serif; }
      #prepaint-shell header { border-bottom:1px solid var(--prepaint-line); }
      /* Device preference still paints the initial frame when JS is unavailable. */
      @media (prefers-color-scheme: dark) {
        html:not([data-theme]) { --prepaint-bg:#151619; --prepaint-ink:#f3f4f7; --prepaint-accent:#91b5ff; --prepaint-line:#383d46; }
      }
      @media (min-width: 640px) {
        #prepaint-brand { left: 24px; }
      }
    </style>

      <script id="experience-prepaint">
      /* Keep these storage keys and colors in step with lib/experience.ts.
         Resolve both independent choices before the first visible frame. */
      (function () {
        try {
          var d = document.documentElement;
          function read(key) { try { return window.localStorage.getItem(key); } catch (e) { return null; } }
          function appearance(value) { return value === 'midnight' ? 'dark' : value === 'system' || value === 'light' || value === 'dark' ? value : null; }
          var experience = read('privatools.experience') === 'play' ? 'play' : 'air';
          var pref = appearance(read('privatools.appearance')) || appearance(read('privatools.daylight.theme')) || 'system';
          var theme = pref === 'system' ? (!window.matchMedia || window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark') : pref;
          d.setAttribute('data-experience', experience);
          d.setAttribute('data-appearance', pref);
          d.setAttribute('data-theme', theme);
          d.style.colorScheme = theme;
          var colors = {air:{light:'#edf3f9',dark:'#151619'},play:{light:'#fae7eb',dark:'#181818'}};
          document.querySelectorAll('meta[name="theme-color"]').forEach(function(meta) { meta.content = colors[experience][theme]; });
        } catch (e) { /* private mode — the CSS media fallback covers it */ }
      })();
    </script>
</head>
  <body>
    <!-- Air and Play share the mounted tools and account state. The visitor's
         chosen experience and appearance are resolved before hydration. -->
    <!-- The only skip link. SkinAppHost retargets it at the app's <main> once
         it mounts; its styles are in the <style> above. -->
    <a id="prepaint-skip" href="#main-content">Skip to main content</a>
    <div id="prepaint-brand" aria-hidden="true">PrivaTools<em>.</em></div>
    <div id="root">
      <div id="prepaint-shell" style="min-height:100vh;font-family:Manrope,system-ui,sans-serif">
        <header style="height:56px"></header>
      </div>
    </div>
    <script type="module" src="/src/main.tsx"></script>
    <!-- Service worker registration lives in src/lib/sw-register.ts so it
         runs in production only and handles updates correctly (skipWaiting +
         controllerchange reload). Don't re-add an inline registration here. -->
    <!-- First-party pageviews and privacy controls are wired once by analyticsBeacon.ts. -->
  </body>
</html>