mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
437 lines
17 KiB
HTML
437 lines
17 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>bitvid | Decentralized Video Sharing</title>
|
|
|
|
<meta
|
|
name="description"
|
|
content="bitvid is a decentralized video hub that prioritizes URL-first playback with WebTorrent fallback so your audience can stream reliably."
|
|
/>
|
|
<meta name="robots" content="index,follow" />
|
|
|
|
<!-- Open Graph Meta Tags -->
|
|
<meta property="og:title" content="bitvid - Decentralized Video Sharing" />
|
|
<meta
|
|
property="og:description"
|
|
content="Share videos and follow creators freely, in a truly decentralized way."
|
|
/>
|
|
<meta property="og:image" content="assets/jpg/bitvid.jpg" />
|
|
<meta property="og:url" content="https://bitvid.network" />
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:locale" content="en_US" />
|
|
|
|
<!-- App Icons -->
|
|
<link rel="icon" href="assets/favicon.ico" sizes="any" />
|
|
<link
|
|
rel="apple-touch-icon"
|
|
sizes="180x180"
|
|
href="assets/png/apple-touch-icon.png"
|
|
/>
|
|
<link
|
|
rel="icon"
|
|
type="image/png"
|
|
sizes="32x32"
|
|
href="assets/png/favicon-32x32.png"
|
|
/>
|
|
<link
|
|
rel="icon"
|
|
type="image/png"
|
|
sizes="16x16"
|
|
href="assets/png/favicon-16x16.png"
|
|
/>
|
|
<link rel="manifest" href="/site.webmanifest" />
|
|
<!--
|
|
Use theme tokens so browsers pick up palette updates without hard-coded
|
|
hex values scattered across templates.
|
|
-->
|
|
<meta name="theme-color" content="var(--color-page)" />
|
|
<script>
|
|
(() => {
|
|
try {
|
|
const STORAGE_KEY = "bitvid:theme";
|
|
const root = document.documentElement;
|
|
const themeMeta = document.querySelector('meta[name="theme-color"]');
|
|
const fallbackColors = {
|
|
dark: "rgb(15, 23, 42)",
|
|
light: "rgb(248, 250, 252)",
|
|
};
|
|
const prefersDark =
|
|
window.matchMedia &&
|
|
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
const storedTheme = window.localStorage.getItem(STORAGE_KEY);
|
|
let resolvedTheme = storedTheme || (prefersDark ? "dark" : "light");
|
|
|
|
if (resolvedTheme === "default") {
|
|
resolvedTheme = "dark";
|
|
}
|
|
|
|
if (resolvedTheme) {
|
|
root.dataset.theme = resolvedTheme;
|
|
}
|
|
|
|
const updateThemeColor = () => {
|
|
if (!themeMeta) {
|
|
return;
|
|
}
|
|
|
|
const computedThemeColor =
|
|
(window.getComputedStyle &&
|
|
getComputedStyle(root).getPropertyValue("--color-page").trim()) ||
|
|
"";
|
|
const metaColor =
|
|
computedThemeColor ||
|
|
fallbackColors[resolvedTheme] ||
|
|
fallbackColors[prefersDark ? "dark" : "light"];
|
|
|
|
if (metaColor) {
|
|
themeMeta.content = metaColor;
|
|
}
|
|
};
|
|
|
|
updateThemeColor();
|
|
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", updateThemeColor, {
|
|
once: true,
|
|
});
|
|
} else if ("requestAnimationFrame" in window) {
|
|
window.requestAnimationFrame(updateThemeColor);
|
|
}
|
|
} catch (error) {
|
|
/* no-op */
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
<!-- Styles -->
|
|
<!--
|
|
Cache busting: keep these query strings in sync with config/asset-version.js
|
|
so browsers pick up the latest sidebar markup and styling without
|
|
hard-refreshing around CDN caches.
|
|
-->
|
|
<link href="css/tailwind.generated.css?v=2024.11.09-2" rel="stylesheet" />
|
|
<link href="css/docs.css?v=2024.11.09-2" rel="stylesheet" />
|
|
</head>
|
|
<body class="sidebar-collapsed" data-ds="new">
|
|
<!--
|
|
SIDEBAR CONTAINER:
|
|
The <aside id="sidebar"> inside handles visibility.
|
|
By default, no special position or z-index on #sidebarContainer.
|
|
-->
|
|
<div id="sidebarContainer">
|
|
<!-- components/sidebar.html gets injected here -->
|
|
</div>
|
|
|
|
<!--
|
|
MAIN CONTENT:
|
|
Layout spacing follows the --sidebar-width custom property on every viewport so the
|
|
collapsible rail always shifts the application shell instead of floating above it.
|
|
-->
|
|
<!--
|
|
`fade-in` is scrubbed by js/index.js after the first animation so that
|
|
re-rendering views does not cause the shell to flicker. Only apply it to
|
|
elements that should animate exactly once on initial paint.
|
|
-->
|
|
|
|
<div
|
|
id="app"
|
|
class="fade-in flex min-h-screen flex-col px-4 pb-8"
|
|
data-sidebar-state="collapsed"
|
|
>
|
|
<!-- Header -->
|
|
<header class="app-shell__header ds-header mb-8 w-full">
|
|
<!-- Logo -->
|
|
<!-- Logo fades in once on initial load. -->
|
|
<a
|
|
href="index.html"
|
|
aria-label="Return to the bitvid home page"
|
|
class="fade-in fade-in-delay-100"
|
|
>
|
|
<span
|
|
class="ds-header__logo inline-flex items-center text-text-strong bv-logo"
|
|
role="img"
|
|
aria-label="bitvid logo"
|
|
>
|
|
<svg
|
|
class="h-full w-auto"
|
|
viewBox="0 0 318.32 108.98"
|
|
aria-hidden="true"
|
|
focusable="false"
|
|
>
|
|
<g class="bv-logo__accent">
|
|
<rect x="7.1" y="7.8" width="5.84" height="5.48" />
|
|
<rect x="7.1" y="18.78" width="24.02" height="5.48" />
|
|
<rect x="7.1" y="29.77" width="42.85" height="5.48" />
|
|
<rect x="7.1" y="40.76" width="61.68" height="5.48" />
|
|
<rect x="7.1" y="51.75" width="80.88" height="5.48" />
|
|
<rect x="7.1" y="62.74" width="61.68" height="5.48" />
|
|
<rect x="7.1" y="73.73" width="42.85" height="5.48" />
|
|
<rect x="7.1" y="84.72" width="24.02" height="5.48" />
|
|
<rect x="7.1" y="95.71" width="5.84" height="5.48" />
|
|
</g>
|
|
<g class="bv-logo__wordmark">
|
|
<path d="M121.28 25.08v44.54c0 5.67 2.79 8.54 8.22 8.54h11.57c5.51 0 8.3-2.87 8.3-8.54v-13.09c0-5.67-2.79-8.46-8.3-8.46h-16.6v-5.83h17.72c8.7 0 13.01 4.47 13.01 13.33v15.01c0 8.86-4.31 13.33-13.01 13.33h-13.73c-8.7 0-13.01-4.47-13.01-13.33V25.08h5.83Z" />
|
|
<path d="M168.53 25.08v6.26h-5.83v-6.26h5.83Zm0 17.56v41.27h-5.83v-41.27h5.83Z" />
|
|
<path d="M191.36 25.08v17.16h9.02v5.19h-9.02v36.48h-5.83v-36.48h-9.82v-5.19h9.82V25.08h5.83Z" />
|
|
<path d="M212.88 42.24l16.84 35.68 16.6-35.68h6.07l-19.56 41.67h-6.31l-20.04-41.67h6.39Z" />
|
|
<path d="M264.05 25.08v6.26h-5.83v-6.26h5.83Zm0 17.16v41.67h-5.83v-41.67h5.83Z" />
|
|
<path d="M311.21 25.08v45.5c0 8.86-4.31 13.33-13.01 13.33h-13.73c-8.7 0-13.01-4.47-13.01-13.33v-15.01c0-8.86 4.31-13.33 13.01-13.33h17.72v5.83h-16.6c-5.51 0-8.3 2.79-8.3 8.46v13.09c0 5.67 2.79 8.54 8.3 8.54h11.49c5.51 0 8.3-2.87 8.3-8.54V25.08h5.83Z" />
|
|
</g>
|
|
</svg>
|
|
</span>
|
|
</a>
|
|
|
|
<div class="header-search__region">
|
|
<form
|
|
id="headerSearchForm"
|
|
role="search"
|
|
class="header-search"
|
|
>
|
|
<button
|
|
type="button"
|
|
class="header-search__filter"
|
|
aria-label="Open search filters"
|
|
>
|
|
<span
|
|
aria-hidden="true"
|
|
class="header-search__icon header-search__icon--filter"
|
|
></span>
|
|
</button>
|
|
<label for="headerSearchInput" class="sr-only">Search videos</label>
|
|
<input
|
|
id="headerSearchInput"
|
|
class="header-search__input"
|
|
type="search"
|
|
name="query"
|
|
placeholder="Search videos"
|
|
autocomplete="off"
|
|
/>
|
|
<button type="submit" class="header-search__submit">
|
|
<span class="sr-only">Submit search</span>
|
|
<span
|
|
aria-hidden="true"
|
|
class="header-search__icon header-search__icon--submit"
|
|
></span>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Buttons on the far right -->
|
|
|
|
<!-- Header action buttons share the fade-in treatment for consistency. -->
|
|
|
|
<div class="ml-auto flex items-center">
|
|
<div class="fade-in fade-in-delay-200 flex items-center space-x-4">
|
|
<button
|
|
id="themeToggle"
|
|
class="header-icon-toggle"
|
|
type="button"
|
|
aria-pressed="false"
|
|
>
|
|
<span class="sr-only">Toggle theme</span>
|
|
<span
|
|
aria-hidden="true"
|
|
class="header-icon-toggle__icon-wrapper"
|
|
data-theme-toggle-icon
|
|
data-theme-icon-state="dark"
|
|
>
|
|
<svg
|
|
class="header-icon-toggle__icon header-icon-toggle__icon--dark"
|
|
viewBox="0 0 24 24"
|
|
role="presentation"
|
|
>
|
|
<path
|
|
d="M10.719 2.082c-2.572 2.028-4.719 5.212-4.719 9.918 0 4.569 1.938 7.798 4.548 9.895-4.829-.705-8.548-4.874-8.548-9.895 0-5.08 3.808-9.288 8.719-9.918zm1.281-2.082c-6.617 0-12 5.383-12 12s5.383 12 12 12c1.894 0 3.87-.333 5.37-1.179-3.453-.613-9.37-3.367-9.37-10.821 0-7.555 6.422-10.317 9.37-10.821-1.74-.682-3.476-1.179-5.37-1.179zm0 10.999c1.437.438 2.562 1.564 2.999 3.001.44-1.437 1.565-2.562 3.001-3-1.436-.439-2.561-1.563-3.001-3-.437 1.436-1.562 2.561-2.999 2.999zm8.001.001c.958.293 1.707 1.042 2 2.001.291-.959 1.042-1.709 1.999-2.001-.957-.292-1.707-1.042-2-2-.293.958-1.042 1.708-1.999 2zm-1-9c-.437 1.437-1.563 2.562-2.998 3.001 1.438.44 2.561 1.564 3.001 3.002.437-1.438 1.563-2.563 2.996-3.002-1.433-.437-2.559-1.564-2.999-3.001z"
|
|
fill="currentColor"
|
|
/>
|
|
</svg>
|
|
<svg
|
|
class="header-icon-toggle__icon header-icon-toggle__icon--light"
|
|
viewBox="0 0 24 24"
|
|
role="presentation"
|
|
>
|
|
<path
|
|
d="M4.069 13h-4.069v-2h4.069c-.041.328-.069.661-.069 1s.028.672.069 1zm3.034-7.312l-2.881-2.881-1.414 1.414 2.881 2.881c.411-.529.885-1.003 1.414-1.414zm11.209 1.414l2.881-2.881-1.414-1.414-2.881 2.881c.528.411 1.002.886 1.414 1.414zm-6.312-3.102c.339 0 .672.028 1 .069v-4.069h-2v4.069c.328-.041.661-.069 1-.069zm0 16c-.339 0-.672-.028-1-.069v4.069h2v-4.069c-.328.041-.661.069-1 .069zm7.931-9c.041.328.069.661.069 1s-.028.672-.069 1h4.069v-2h-4.069zm-3.033 7.312l2.88 2.88 1.415-1.414-2.88-2.88c-.412.528-.886 1.002-1.415 1.414zm-11.21-1.415l-2.88 2.88 1.414 1.414 2.88-2.88c-.528-.411-1.003-.885-1.414-1.414zm2.312-4.897c0 2.206 1.794 4 4 4s4-1.794 4-4-1.794-4-4-4-4 1.794-4 4zm10 0c0 3.314-2.686 6-6 6s-6-2.686-6-6 2.686-6 6-6 6 2.686 6 6z"
|
|
fill="currentColor"
|
|
/>
|
|
</svg>
|
|
</span>
|
|
</button>
|
|
<!-- Login Button -->
|
|
<button
|
|
id="loginButton"
|
|
class="btn focus-ring h-12 w-12 rounded-full p-0"
|
|
aria-label="Open login modal"
|
|
>
|
|
<svg
|
|
class="h-9 w-9"
|
|
viewBox="0 0 24 24"
|
|
role="presentation"
|
|
focusable="false"
|
|
aria-hidden="true"
|
|
>
|
|
<path
|
|
d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0Zm0 22c-3.123 0-5.914-1.441-7.749-3.69.259-.588.783-.995 1.867-1.246 2.244-.518 4.459-.981 3.393-2.945C6.356 8.299 8.612 5 12 5s4.366 3.177 1.489 9.119c-1.035 1.952 1.1 2.416 3.393 2.945 1.082.25 1.61.655 1.871 1.241C17.917 20.558 15.125 22 12 22Z"
|
|
fill="var(--color-bg)"
|
|
fill-rule="nonzero"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
|
|
<!-- Upload Button (hidden by default) -->
|
|
<button
|
|
id="uploadButton"
|
|
class="btn focus-ring hidden h-12 w-12 rounded-full p-0 text-xl font-bold leading-none"
|
|
>
|
|
+
|
|
</button>
|
|
|
|
<!-- Profile Button (hidden by default) -->
|
|
<button
|
|
id="profileButton"
|
|
class="btn-ghost focus-ring relative hidden h-12 w-12 rounded-full p-0"
|
|
>
|
|
<div class="h-10 w-10 overflow-hidden rounded-full">
|
|
<img
|
|
id="profileAvatar"
|
|
src="assets/svg/default-profile.svg"
|
|
alt="Profile"
|
|
class="h-full w-full object-cover"
|
|
/>
|
|
</div>
|
|
<span id="profileUnreadDot" aria-hidden="true"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Notification Portal -->
|
|
<div
|
|
id="notificationPortal"
|
|
class="notification-portal"
|
|
role="region"
|
|
aria-live="polite"
|
|
aria-label="System notifications"
|
|
>
|
|
<!-- Error Container -->
|
|
<div
|
|
id="errorContainer"
|
|
class="notification-banner notification-banner--critical hidden"
|
|
data-state="critical"
|
|
></div>
|
|
|
|
<!-- Status Container -->
|
|
<div
|
|
id="statusContainer"
|
|
class="notification-banner notification-banner--info status-banner hidden"
|
|
role="status"
|
|
aria-live="polite"
|
|
>
|
|
<span class="status-spinner" aria-hidden="true"></span>
|
|
<span class="status-message" data-status-message></span>
|
|
</div>
|
|
|
|
<!-- Success Container -->
|
|
<div
|
|
id="successContainer"
|
|
class="notification-banner notification-banner--success hidden"
|
|
></div>
|
|
</div>
|
|
|
|
<!-- Dynamic views (like most-recent-videos.html, etc.) -->
|
|
<main id="viewContainer" class="mb-8 flex-grow"></main>
|
|
|
|
<!-- Modal Container (external modals) -->
|
|
<div id="modalContainer"></div>
|
|
|
|
<!-- Tagline / Slogan -->
|
|
|
|
<!-- Tagline fade matches the cards. -->
|
|
|
|
<div class="fade-in fade-in-delay-300 mb-8 text-center">
|
|
<h2 class="text-2xl font-bold tracking-wide text-muted-strong">
|
|
seed. zap. subscribe.
|
|
</h2>
|
|
</div>
|
|
|
|
<!-- Mobile Search FAB -->
|
|
<button
|
|
id="mobileSearchFab"
|
|
class="focus-ring fixed bottom-6 right-6 z-50 flex h-14 w-14 items-center justify-center rounded-full bg-accent-strong shadow-lg transition-transform duration-200 hover:scale-105 active:scale-95 md:hidden"
|
|
aria-label="Open search"
|
|
>
|
|
<svg
|
|
class="h-6 w-6 text-white"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2.5"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<circle cx="11" cy="11" r="8"></circle>
|
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
</svg>
|
|
</button>
|
|
|
|
<!-- Mobile Search Container -->
|
|
<div
|
|
id="mobileSearchContainer"
|
|
class="fixed top-4 left-4 right-4 z-50 hidden max-w-sm mx-auto origin-top transition-all duration-200 md:hidden"
|
|
>
|
|
<form
|
|
id="mobileSearchForm"
|
|
role="search"
|
|
class="flex items-center gap-3 rounded-full border border-border bg-overlay-panel bg-opacity-95 p-2 shadow-xl backdrop-blur-md"
|
|
>
|
|
<button
|
|
type="button"
|
|
class="header-search__filter shrink-0"
|
|
aria-label="Open search filters"
|
|
>
|
|
<span
|
|
aria-hidden="true"
|
|
class="header-search__icon header-search__icon--filter"
|
|
></span>
|
|
</button>
|
|
<label for="mobileSearchInput" class="sr-only">Search videos</label>
|
|
<input
|
|
id="mobileSearchInput"
|
|
class="header-search__input min-w-0 flex-1 bg-transparent px-2 text-base text-text outline-none placeholder:text-muted"
|
|
type="search"
|
|
name="query"
|
|
placeholder="Search videos"
|
|
autocomplete="off"
|
|
/>
|
|
<button type="submit" class="header-search__submit shrink-0">
|
|
<span class="sr-only">Submit search</span>
|
|
<span
|
|
aria-hidden="true"
|
|
class="header-search__icon header-search__icon--submit"
|
|
></span>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Additional Scripts -->
|
|
<!-- Bootstraps nostr-tools before other modules rely on nostrToolsReady -->
|
|
<script type="module" src="js/nostrToolsBootstrap.js?v=2024.11.09"></script>
|
|
<script type="module" src="js/config.js?v=2024.11.09"></script>
|
|
<script type="module" src="js/lists.js?v=2024.11.09"></script>
|
|
<script type="module" src="js/accessControl.js?v=2024.11.09"></script>
|
|
<script type="module" src="js/webtorrent.js?v=2024.11.09"></script>
|
|
<script type="module" src="js/viewManager.js?v=2024.11.09"></script>
|
|
<script type="module" src="js/bootstrap.js?v=2024.11.09"></script>
|
|
<script type="module" src="js/disclaimer.js?v=2024.11.09"></script>
|
|
<script type="module" src="js/index.js?v=2024.11.09"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
|
|
<div id="uiOverlay" data-overlay-root aria-hidden="true"></div>
|
|
</body>
|
|
</html>
|