Skip to content

Update frontend #4

Description

@hypractive

Ive made you guys a free frontend, completely by me. #antiai

replace index.html with:

<title>Static Proxy</title>

<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content="#08090c"

<style> :root { --text: #ffffff; --muted: rgba(255,255,255,.62); --glass: rgba(10,10,14,.34); --border: rgba(255,255,255,.14); --button: rgba(255,255,255,.10); --button-hover: rgba(255,255,255,.18); } body.light { --text: #101014; --muted: rgba(16,16,20,.60); --glass: rgba(255,255,255,.42); --border: rgba(0,0,0,.10); --button: rgba(0,0,0,.07); --button-hover: rgba(0,0,0,.13); } * { box-sizing: border-box; } html, body { width: 100%; min-height: 100%; margin: 0; } body { min-height: 100vh; overflow-x: hidden; color: var(--text); font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #08090c; } /* ========================================= BACKGROUND ========================================= */ #shader-bg { position: fixed; inset: 0; width: 100%; height: 100%; z-index: -10; overflow: hidden; background: #08090c; } #shader-bg canvas { display: block; width: 100%; height: 100%; } .background-overlay { position: fixed; inset: 0; z-index: -9; pointer-events: none; background: radial-gradient( circle at 50% 40%, transparent 0%, rgba(0,0,0,.08) 42%, rgba(0,0,0,.52) 100% ); } body.light .background-overlay { background: radial-gradient( circle at 50% 40%, transparent 0%, rgba(255,255,255,.10) 48%, rgba(255,255,255,.42) 100% ); } /* ========================================= TOP RIGHT ========================================= */ .top-controls { position: fixed; top: 20px; right: 20px; z-index: 100; display: flex; align-items: center; gap: 8px; } .control { height: 42px; padding: 0 14px; display: flex; align-items: center; justify-content: center; gap: 8px; color: var(--text); background: var(--glass); border: 1px solid var(--border); border-radius: 14px; backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); box-shadow: 0 8px 30px rgba(0,0,0,.12); cursor: pointer; transition: color .35s ease, background .35s ease, border-color .35s ease, transform .2s ease; } .control:hover { background: var(--button-hover); transform: translateY(-1px); } .control svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; transition: transform .55s cubic-bezier(.34,1.56,.64,1); } #themeToggle:active svg { transform: rotate(180deg) scale(.78); } /* ========================================= THEME TRANSITION ========================================= */ .theme-transition { position: fixed; width: 24px; height: 24px; z-index: 99999; border-radius: 50%; pointer-events: none; transform: translate(-50%, -50%) scale(0); background: #08090c; animation: themeExpand .72s cubic-bezier(.76,0,.24,1) forwards; } .theme-transition.light { background: #f3f5f7; } @Keyframes themeExpand { from { transform: translate(-50%, -50%) scale(0); } to { transform: translate(-50%, -50%) scale(180); } } /* ========================================= HISTORY ========================================= */ .history-wrapper { position: relative; } .history-panel { position: absolute; top: 51px; right: 0; width: 300px; padding: 12px; display: none; color: var(--text); background: var(--glass); border: 1px solid var(--border); border-radius: 18px; backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); box-shadow: 0 20px 70px rgba(0,0,0,.25); } .history-panel.open { display: block; animation: historyIn .18s ease-out; } @Keyframes historyIn { from { opacity: 0; transform: translateY(-6px) scale(.97); } to { opacity: 1; transform: translateY(0) scale(1); } } .history-header { display: flex; align-items: center; justify-content: space-between; padding: 5px 6px 10px; color: var(--muted); font-size: 11px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; } .clear-history { padding: 0; border: 0; background: transparent; color: var(--muted); font-size: 11px; cursor: pointer; } .clear-history:hover { color: var(--text); } .history-list { max-height: 260px; overflow-y: auto; } .history-item { width: 100%; padding: 10px; border: 0; border-radius: 10px; color: var(--text); background: transparent; text-align: left; font: inherit; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: background .2s ease; } .history-item:hover { background: var(--button); } .empty-history { padding: 20px 10px; color: var(--muted); text-align: center; font-size: 13px; } /* ========================================= PAGE ========================================= */ .page { min-height: 100vh; display: flex; flex-direction: column; } main { flex: 1; width: min( 900px, calc(100% - 40px) ); margin: auto; padding: 120px 0 70px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; } .badge { margin-bottom: 22px; padding: 7px 12px; border: 1px solid var(--border); border-radius: 999px; background: var(--glass); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); color: var(--muted); font-size: 11px; font-weight: 600; letter-spacing: .13em; text-transform: uppercase; } h1 { margin: 0; font-size: clamp( 52px, 9vw, 96px ); line-height: .88; letter-spacing: -.075em; font-weight: 800; text-shadow: 0 10px 50px rgba(0,0,0,.18); } .description { max-width: 560px; margin: 25px auto 34px; color: var(--muted); font-size: 16px; line-height: 1.6; } /* ========================================= SEARCH ========================================= */ .search-box { width: min( 680px, 100% ); padding: 7px; display: flex; align-items: center; border: 1px solid var(--border); border-radius: 18px; background: var(--glass); backdrop-filter: blur(22px); -webkit-backdrop-filter: blur(22px); box-shadow: 0 20px 70px rgba(0,0,0,.25); } .search-input { min-width: 0; flex: 1; height: 50px; padding: 0 16px; border: 0; outline: 0; color: var(--text); background: transparent; font: inherit; font-size: 15px; } .search-input::placeholder { color: var(--muted); } .search-button, .about-button { height: 50px; padding: 0 20px; border: 0; border-radius: 13px; color: var(--text); background: var(--button); font: inherit; font-weight: 600; cursor: pointer; transition: background .2s ease, transform .2s ease; } .search-button:hover, .about-button:hover { background: var(--button-hover); transform: translateY(-1px); } .about-button { height: 44px; margin-top: 14px; font-size: 13px; } /* ========================================= MARQUEE ========================================= */ .marquee { width: 100%; overflow: hidden; padding: 25px 0; opacity: .7; mask-image: linear-gradient( to right, transparent, black 12%, black 88%, transparent ); -webkit-mask-image: linear-gradient( to right, transparent, black 12%, black 88%, transparent ); } .marquee-track { display: flex; width: max-content; animation: marquee 24s linear infinite; } .marquee-row { display: flex; white-space: nowrap; } .marquee-text { margin: 0 45px; font-size: clamp( 35px, 6vw, 75px ); line-height: .9; font-weight: 800; letter-spacing: -.07em; } @Keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } } /* ========================================= FOOTER ========================================= */ footer { padding: 0 20px 25px; text-align: center; } footer p { margin: 0; color: var(--muted); font-size: 11px; letter-spacing: .08em; text-transform: lowercase; } footer span { color: var(--text); } /* ========================================= MOBILE ========================================= */ @media (max-width: 600px) { .top-controls { top: 14px; right: 14px; } .control { height: 38px; padding: 0 11px; } main { width: calc(100% - 28px); padding-top: 110px; } .search-box { flex-direction: column; } .search-input { width: 100%; } .search-button { width: 100%; } .history-panel { right: -5px; width: calc(100vw - 28px); } } @media (prefers-reduced-motion: reduce) { .marquee-track { animation: none; } .theme-transition { animation-duration: .2s; } } </style>
<!-- History -->
<div class="history-wrapper">

  <button
    class="control"
    id="historyToggle"
    type="button"
    aria-label="Search history"
  >
    <svg viewBox="0 0 24 24">
      <path d="M3 12a9 9 0 1 0 3-6.7"></path>
      <path d="M3 4v5h5"></path>
      <path d="M12 7v5l3 2"></path>
    </svg>

    <span>History</span>
  </button>

  <div
    class="history-panel"
    id="historyPanel"
  >
    <div class="history-header">
      <span>Recent searches</span>

      <button
        class="clear-history"
        id="clearHistory"
        type="button"
      >
        Clear
      </button>
    </div>

    <div
      class="history-list"
      id="historyList"
    ></div>
  </div>

</div>

<!-- Theme -->
<button
  class="control"
  id="themeToggle"
  type="button"
  aria-label="Toggle light and dark mode"
>
  <svg
    id="themeIcon"
    viewBox="0 0 24 24"
  >
    <circle
      cx="12"
      cy="12"
      r="4"
    ></circle>

    <path d="M12 2v2"></path>
    <path d="M12 20v2"></path>
    <path d="m4.93 4.93 1.41 1.41"></path>
    <path d="m17.66 17.66 1.41 1.41"></path>
    <path d="M2 12h2"></path>
    <path d="M20 12h2"></path>
    <path d="m4.93 19.07 1.41-1.41"></path>
    <path d="m17.66 6.34 1.41-1.41"></path>
  </svg>

  <span id="themeText">Light</span>
</button>
<!-- =======================================
     MAIN
======================================== -->

<main>

  <div class="badge">
    Static Proxy
  </div>

  <h1>
    Browse<br>
    freely.
  </h1>

  <p class="description">
    A minimal, fast, and beautifully designed web interface.
  </p>

  <!--
    IMPORTANT:
    These class names are kept compatible with
    the existing Ultraviolet index.js setup.
  -->
  <form class="search-box" id="uv-form">

    <input
      class="search-input"
      id="uv-address"
      type="text"
      autocomplete="off"
      placeholder="Search the web freely..."
      aria-label="Search"
    >

    <button
      class="search-button"
      type="submit"
    >
      Search
    </button>

  </form>

  <button
    class="about-button"
    id="aboutButton"
    type="button"
  >
    About:Blank
  </button>

</main>


<!-- =======================================
     MARQUEE
======================================== -->

<section
  class="marquee"
  aria-hidden="true"
>
  <div class="marquee-track">

    <div class="marquee-row">

      <div class="marquee-text">
        Star the repo if you like it
      </div>

      <div class="marquee-text">
        Share it if you like it
      </div>

      <div class="marquee-text">
        Star the repo if you like it
      </div>

      <div class="marquee-text">
        Share it if you like it
      </div>

    </div>

    <div class="marquee-row">

      <div class="marquee-text">
        Star the repo if you like it
      </div>

      <div class="marquee-text">
        Share it if you like it
      </div>

      <div class="marquee-text">
        Star the repo if you like it
      </div>

      <div class="marquee-text">
        Share it if you like it
      </div>

    </div>

  </div>
</section>


<!-- =======================================
     FOOTER
======================================== -->

<footer>
  <p>
    designed by
    <span>hypractive</span>,
    built by
    <span>carbonnetwork</span>
  </p>
</footer>
<script> const body = document.body; const themeToggle = document.getElementById("themeToggle"); const themeText = document.getElementById("themeText"); const themeIcon = document.getElementById("themeIcon"); const historyToggle = document.getElementById("historyToggle"); const historyPanel = document.getElementById("historyPanel"); const historyList = document.getElementById("historyList"); const clearHistory = document.getElementById("clearHistory"); const searchInput = document.getElementById("uv-address"); const THEME_KEY = "staticProxyTheme"; const HISTORY_KEY = "staticProxyHistory"; /* ----------------------------------------- THEME ----------------------------------------- */ function setTheme(theme) { body.classList.toggle( "light", theme === "light" ); if (theme === "light") { themeText.textContent = "Dark"; themeIcon.innerHTML = ` `; } else { themeText.textContent = "Light"; themeIcon.innerHTML = ` `; } localStorage.setItem( THEME_KEY, theme ); } const savedTheme = localStorage.getItem( THEME_KEY ); if (savedTheme) { setTheme(savedTheme); } else { setTheme( window.matchMedia( "(prefers-color-scheme: light)" ).matches ? "light" : "dark" ); } /* ----------------------------------------- ANIMATED THEME SWITCH ----------------------------------------- */ themeToggle.addEventListener( "click", () => { const rect = themeToggle.getBoundingClientRect(); const x = rect.left + rect.width / 2; const y = rect.top + rect.height / 2; const nextTheme = body.classList.contains("light") ? "dark" : "light"; const transition = document.createElement("div"); transition.className = "theme-transition " + ( nextTheme === "light" ? "light" : "" ); transition.style.left = `${x}px`; transition.style.top = `${y}px`; document.body.appendChild( transition ); setTimeout( () => { setTheme(nextTheme); }, 90 ); setTimeout( () => { transition.remove(); }, 800 ); } ); /* ----------------------------------------- HISTORY ----------------------------------------- */ function getHistory() { try { return JSON.parse( localStorage.getItem( HISTORY_KEY ) || "[]" ); } catch { return []; } } function saveHistory(items) { localStorage.setItem( HISTORY_KEY, JSON.stringify( items.slice(0, 20) ) ); } function escapeHTML(value) { return value .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function addToHistory(value) { const clean = value.trim(); if (!clean) return; let history = getHistory(); history = history.filter( item => item !== clean ); history.unshift(clean); saveHistory(history); renderHistory(); } function renderHistory() { const history = getHistory(); if (!history.length) { historyList.innerHTML = `
No searches yet.
`; return; } historyList.innerHTML = history.map( item => ` ${escapeHTML(item)} ` ).join(""); document .querySelectorAll(".history-item") .forEach(button => { button.addEventListener( "click", () => { searchInput.value = button.dataset.value; historyPanel .classList .remove("open"); searchInput.focus(); } ); }); } historyToggle.addEventListener( "click", event => { event.stopPropagation(); historyPanel .classList .toggle("open"); renderHistory(); } ); clearHistory.addEventListener( "click", () => { localStorage.removeItem( HISTORY_KEY ); renderHistory(); } ); document.addEventListener( "click", event => { if ( !event.target.closest( ".history-wrapper" ) ) { historyPanel .classList .remove("open"); } } ); /* Save searches when the existing UV form is submitted. index.js remains responsible for actually processing the request. */ document .getElementById("uv-form") .addEventListener( "submit", () => { if ( searchInput.value.trim() ) { addToHistory( searchInput.value ); } } ); renderHistory(); </script> <script> let aboutBlankWindow = null; document .getElementById("aboutButton") .addEventListener( "click", () => { if ( aboutBlankWindow && !aboutBlankWindow.closed ) { aboutBlankWindow.focus(); return; } aboutBlankWindow = window.open( "about:blank", "_blank" ); if (!aboutBlankWindow) { return; } aboutBlankWindow.document.body.style.margin = "0"; aboutBlankWindow.document.body.style.height = "100vh"; const iframe = aboutBlankWindow .document .createElement("iframe"); iframe.style.border = "none"; iframe.style.width = "100%"; iframe.style.height = "100%"; iframe.style.margin = "0"; /* Opens this page inside the new about:blank tab. */ iframe.src = window.location.href; aboutBlankWindow .document .body .appendChild(iframe); } ); </script> <script> const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); document .getElementById("shader-bg") .appendChild(canvas); let width = 0; let height = 0; let time = 0; function resizeCanvas() { width = window.innerWidth; height = window.innerHeight; const dpr = Math.min( window.devicePixelRatio || 1, 2 ); canvas.width = width * dpr; canvas.height = height * dpr; canvas.style.width = width + "px"; canvas.style.height = height + "px"; ctx.setTransform( dpr, 0, 0, dpr, 0, 0 ); } window.addEventListener( "resize", resizeCanvas ); resizeCanvas(); function drawBackground() { time += .003; ctx.clearRect( 0, 0, width, height ); const light = body.classList.contains( "light" ); const base = ctx.createRadialGradient( width * .5, height * .4, 0, width * .5, height * .5, Math.max( width, height ) ); if (light) { base.addColorStop( 0, "#f4f6f7" ); base.addColorStop( .45, "#dbe5e8" ); base.addColorStop( 1, "#aab7be" ); } else { base.addColorStop( 0, "#202832" ); base.addColorStop( .45, "#10151d" ); base.addColorStop( 1, "#050609" ); } ctx.fillStyle = base; ctx.fillRect( 0, 0, width, height ); const blobs = [ { x: .25 + Math.sin( time * .8 ) * .08, y: .3 + Math.cos(time) * .08, color: "115,191,196" }, { x: .75 + Math.cos( time * .7 ) * .09, y: .4 + Math.sin( time * .8 ) * .1, color: "255,129,10" }, { x: .52 + Math.sin( time * .5 ) * .1, y: .75 + Math.cos( time * .7 ) * .08, color: "141,160,206" } ]; blobs.forEach(blob => { const gradient = ctx.createRadialGradient( width * blob.x, height * blob.y, 0, width * blob.x, height * blob.y, Math.max( width, height ) * .42 ); gradient.addColorStop( 0, `rgba( ${blob.color}, ${light ? ".18" : ".30"} )` ); gradient.addColorStop( 1, "rgba(0,0,0,0)" ); ctx.fillStyle = gradient; ctx.fillRect( 0, 0, width, height ); }); requestAnimationFrame( drawBackground ); } drawBackground(); </script> <script src="uv/uv.bundle.js"></script> <script src="uv/uv.config.js"></script> <script src="index.js"></script>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions