sean2077_obsidian-dynamic-t.../docs/index.html

800 lines
46 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Obsidian DTB - Dynamic Theme Background</title>
<meta name="description"
content="Dynamic Theme Background (DTB) - Multi-source dynamic wallpaper plugin for Obsidian: Unsplash, Pexels, Pixabay, Wallhaven, 360 Wallpaper, custom & local images.">
<meta name="keywords"
content="Obsidian, wallpaper, background, plugin, Unsplash, Pexels, Pixabay, Wallhaven, 360 Wallpaper, dynamic, theme background">
<meta name="author" content="sean2077">
<meta name="robots" content="index,follow">
<meta name="theme-color" content="#0a0a0a">
<link rel="canonical" href="https://obsidian-dynamic-theme-background.pages.dev">
<meta property="og:type" content="website">
<meta property="og:title" content="Dynamic Theme Background - Obsidian Plugin">
<meta property="og:description"
content="Multi-source wallpapers, smart scheduling & visual tuning for an inspired Obsidian workspace.">
<meta property="og:url" content="https://obsidian-dynamic-theme-background.pages.dev">
<meta property="og:image"
content="https://raw.githubusercontent.com/sean2077/obsidian-dynamic-theme-background/master/docs/assets/dtb-status-bar.jpg">
<meta property="og:image:type" content="image/jpeg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:site_name" content="Dynamic Theme Background">
<meta property="og:locale" content="en_US">
<meta name="msapplication-TileColor" content="#667eea">
<meta name="mobile-web-app-capable" content="yes">
<link rel="manifest" href="manifest.json">
<!-- Preconnect for faster image fetch -->
<link rel="preconnect" href="https://raw.githubusercontent.com" crossorigin>
<link rel="preconnect" href="https://api.github.com" crossorigin>
<link rel="dns-prefetch" href="https://github.com">
<!-- Favicon -->
<link rel="icon"
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌈</text></svg>">
<link rel="mask-icon"
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌈</text></svg>"
color="#667eea">
<!-- Structured Data -->
<script type="application/ld+json" id="schema-dtb">{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Dynamic Theme Background",
"applicationCategory": "ProductivityApplication",
"operatingSystem": "Windows, macOS, Linux, iOS, Android",
"abstract": "Dynamic multi-source wallpaper management for Obsidian.",
"description": "Obsidian plugin providing dynamic multi-source wallpapers with smart scheduling, visual tuning, caching, time-based rotation and custom sources.",
"author": {"@type": "Person", "name": "sean2077"},
"publisher": {"@type": "Organization", "name": "Obsidian Community"},
"license": "https://opensource.org/licenses/MIT",
"keywords": ["Obsidian","wallpaper","background","unsplash","pexels","pixabay","wallhaven","dynamic","plugin"],
"url": "https://github.com/sean2077/obsidian-dynamic-theme-background",
"sourceCode": "https://github.com/sean2077/obsidian-dynamic-theme-background",
"downloadUrl": "https://github.com/sean2077/obsidian-dynamic-theme-background/releases",
"image": "https://raw.githubusercontent.com/sean2077/obsidian-dynamic-theme-background/master/docs/assets/dtb-status-bar.jpg",
"featureList": [
"Multiple providers (Unsplash, Pexels, Pixabay, Wallhaven, custom)",
"Local + remote mixing",
"Time-based rotation & interval scheduling",
"Visual adjustments (blur, brightness, saturation, hue)",
"Caching & performance optimization",
"Command palette quick actions",
"Custom API support"
],
"sameAs": ["https://obsidian.md/plugins","https://github.com/sean2077"],
"offers": {"@type": "Offer", "price": "0", "priceCurrency": "USD"}
}</script>
<!-- External CSS -->
<link rel="stylesheet" href="landing.css?v=1733500901">
<!-- Early performance & iOS detection (must be inline for immediate execution) -->
<script>
(function () {
var ua = navigator.userAgent;
var isIOS = /iPhone|iPad|iPod/i.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
var isLowPerf = false;
try {
isLowPerf = navigator.hardwareConcurrency <= 2 ||
navigator.deviceMemory <= 2 ||
/Android|iPhone|iPad|iPod/i.test(ua) ||
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
} catch (e) { }
if (isLowPerf) document.documentElement.classList.add('dtb-perf-low');
if (isIOS) document.documentElement.classList.add('dtb-ios');
})();
</script>
</head>
<body>
<a href="#main" class="skip-link" data-i18n="accessibility.skipToContent">Skip to content</a>
<div class="dynamic-bg" aria-hidden="true"></div>
<div class="wallpaper-overlay"></div>
<div class="floating-particles" aria-hidden="true"></div>
<header class="header">
<nav class="nav container">
<a href="#top" class="logo" aria-label="Back to top / Home" data-i18n-title="nav.backToTop">
<span aria-hidden="true">🌈</span> <span data-i18n="nav.brandName">Obsidian DTB</span>
<span class="version-badge" id="dtb-version-header" data-i18n-title="nav.currentVersion"
data-fallback="v?" data-i18n="nav.versionLoading">loading…</span>
</a>
<div class="nav-links">
<a href="#demo" data-i18n="nav.preview">Preview</a>
<a href="#features" data-i18n="nav.features">Features</a>
<a href="#installation" data-i18n="nav.install">Install</a>
<a href="#quickstart" data-i18n="nav.quickstart">Quick Start</a>
<a href="#sources" data-i18n="nav.sources">Sources</a>
<a href="#faq" data-i18n="nav.faq">FAQ</a>
<a class="icon-btn" href="https://github.com/sean2077/obsidian-dynamic-theme-background" target="_blank"
rel="noopener noreferrer" data-i18n-aria-label="nav.github" data-i18n-title="nav.github">
<svg viewBox="0 0 16 16" aria-hidden="true" role="img" focusable="false">
<path
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0016 8c0-4.42-3.58-8-8-8Z" />
</svg>
</a>
<button class="icon-btn lang-toggle" id="lang-toggle-btn" data-i18n-aria-label="nav.langSwitch"
data-i18n-title="nav.langSwitchTitle">
<span class="ico" aria-hidden="true">🌐</span>
<span class="lang-text" aria-hidden="true"></span>
</button>
</div>
</nav>
</header>
<main id="main" tabindex="-1">
<!-- Hero Section -->
<section class="hero" id="top">
<div class="hero-overlay"></div>
<div class="container hero-container">
<div class="hero-badges">
<a href="https://obsidian.md" target="_blank" rel="noopener noreferrer" class="hero-badge"
data-i18n="hero.badge">Obsidian Plugin</a>
<span class="hero-badge hero-badge-alt" data-i18n="hero.desc">Multi-source Dynamic Wallpaper</span>
</div>
<h1 class="hero-title">
<span class="hero-title-text">Dynamic Theme</span>
<span class="hero-title-text hero-title-animated">Background</span>
</h1>
<p class="hero-slogan" data-i18n="hero.slogan">Build Your Own Wallpaper Library in Obsidian!</p>
<p class="hero-tagline" data-i18n="hero.tagline">Make every note-taking experience visually inspiring
</p>
<div class="hero-stats">
<div class="hero-stat">
<span class="hero-stat-number">7+</span>
<span class="hero-stat-label" data-i18n="hero.statSources">Wallpaper Sources</span>
</div>
<div class="hero-stat-divider"></div>
<div class="hero-stat">
<span class="hero-stat-number"></span>
<span class="hero-stat-label" data-i18n="hero.statImages">HD Wallpapers</span>
</div>
<div class="hero-stat-divider"></div>
<div class="hero-stat">
<span class="hero-stat-number">100%</span>
<span class="hero-stat-label" data-i18n="hero.statFree">Free & Open Source</span>
</div>
</div>
<div class="cta-buttons">
<a href="#installation" class="btn btn-primary"><span></span><span
data-i18n="hero.installBtn">Install Now</span></a>
<a href="#demo" class="btn btn-ghost"><span></span><span data-i18n="hero.watchDemo">Watch
Demo</span></a>
</div>
<div class="hero-scroll-hint">
<span data-i18n="hero.scrollHint">Scroll to explore</span>
<div class="hero-scroll-arrow"></div>
</div>
</div>
</section>
<!-- Demo Section -->
<section class="demo full-section" id="demo">
<div class="container">
<h2 class="fade-in">🎬 <span class="heading-text" data-i18n="demo.title">Preview</span></h2>
<div class="demo-gallery fade-in">
<div class="demo-item">
<div class="demo-img-wrap">
<img src="assets/dtb-demo2.gif" alt="Wallpaper switching preview" class="demo-img"
width="800" height="450" loading="lazy" decoding="async">
<span class="demo-img-label" data-i18n="demo.switchingLabel">Switching</span>
</div>
<h3 data-i18n="demo.switchingTitle">Multisource Library</h3>
<ul class="demo-list" data-demo-list="switching"></ul>
</div>
<div class="demo-item">
<div class="demo-img-wrap">
<img src="assets/dtb-demo.gif" alt="Image backgrounds and gradient backgrounds"
class="demo-img" width="800" height="450" loading="lazy" decoding="async">
<span class="demo-img-label" data-i18n="demo.backgroundsLabel">Backgrounds</span>
</div>
<h3 data-i18n="demo.backgroundsTitle">Image & Gradient Backgrounds</h3>
<ul class="demo-list" data-demo-list="backgrounds"></ul>
</div>
<div class="demo-item">
<div class="demo-img-wrap">
<img src="assets/dtb-demo3.gif" alt="Feature-rich settings panel" class="demo-img"
width="800" height="450" loading="lazy" decoding="async">
<span class="demo-img-label" data-i18n="demo.settingsLabel">Settings</span>
</div>
<h3 data-i18n="demo.settingsTitle">Feature-rich Settings</h3>
<ul class="demo-list" data-demo-list="settings"></ul>
</div>
</div>
<div class="summary-pills fade-in">
<div class="summary-pill"><span class="ico">🌐</span><span
data-i18n="demo.pills.multiSourceMix">Multi-Source Mix</span></div>
<div class="summary-pill"><span class="ico">🎚️</span><span data-i18n="demo.pills.liveTuning">Live
Tuning</span></div>
<div class="summary-pill"><span class="ico">🧠</span><span
data-i18n="demo.pills.cachingPrefetch">Caching & Prefetch</span></div>
<div class="summary-pill"><span class="ico">⌨️</span><span
data-i18n="demo.pills.commandPalette">Command Palette</span></div>
<div class="summary-pill"><span class="ico">🔧</span><span data-i18n="demo.pills.customApi">Custom
API</span></div>
</div>
</div>
</section>
<!-- Features Section -->
<section class="features full-section" id="features">
<div class="container">
<h2 class="fade-in section-title"><span class="heading-text" data-i18n="features.title">Key
Features</span></h2>
<p class="features-subtitle fade-in" data-i18n="features.subtitle">An elevated wallpaper experience
crafted for Obsidian</p>
<div class="features-grid">
<div class="feature-card fade-in" style="--accent:#667eea;">
<h3><span class="feature-icon">🖼️</span><span
data-i18n="features.multiSource.title">Multi-source Library</span></h3>
<p data-i18n="features.multiSource.desc">Unsplash, Pixabay, Pexels, Wallhaven + custom API &
local folders—flexibly combine and curate.</p>
</div>
<div class="feature-card fade-in" style="--accent:#14b8a6;">
<h3><span class="feature-icon">🎨</span><span data-i18n="features.visualTuning.title">Visual
Tuning</span></h3>
<p data-i18n="features.visualTuning.desc">Adjust blur, brightness, saturation, hue &
more—theme-friendly and dark/light aware.</p>
</div>
<div class="feature-card fade-in" style="--accent:#f59e0b;">
<h3><span class="feature-icon"></span><span data-i18n="features.smartRotation.title">Smart
Rotation</span></h3>
<p data-i18n="features.smartRotation.desc">Time windows, interval auto-change, command palette
quick switch, minute-level scheduling.</p>
</div>
<div class="feature-card fade-in" style="--accent:#8b5cf6;">
<h3><span class="feature-icon">🤝</span><span data-i18n="features.greatUx.title">Great UX</span>
</h3>
<p data-i18n="features.greatUx.desc">Async loading, caching, default presets, batch ops &
shortcut commands for fast workflows.</p>
</div>
<div class="feature-card fade-in" style="--accent:#f43f5e;">
<h3><span class="feature-icon">🚀</span><span data-i18n="features.performance.title">Performance
Minded</span></h3>
<p data-i18n="features.performance.desc">Optimized rendering & caching for minimal impact on
editor responsiveness.</p>
</div>
<div class="feature-card fade-in" style="--accent:#06b6d4;">
<h3><span class="feature-icon">📦</span><span data-i18n="features.roadmap.title">Roadmap</span>
</h3>
<p data-i18n="features.roadmap.desc">Weather integration, theme packs, video / audio / AI
backgrounds, mobile refinements.</p>
</div>
</div>
</div>
</section>
<!-- Installation Section -->
<section class="installation full-section" id="installation">
<div class="container">
<h2 class="fade-in section-title">⬇️ <span class="heading-text"
data-i18n="installation.title">Installation</span></h2>
<p class="features-subtitle fade-in" data-i18n="installation.subtitle">
Pick a method below. Once enabled, jump to Quick Start to configure rotation & sources.</p>
<div class="install-tabs fade-in" id="install-tabs" data-default="market">
<div class="tab-list" role="tablist" aria-label="Installation methods">
<button class="tab-btn" role="tab" aria-selected="true" data-tab="market" id="tab-market">🛒
<span data-i18n="installation.tabs.marketplace">Marketplace</span></button>
<button class="tab-btn" role="tab" aria-selected="false" data-tab="brat" id="tab-brat">🧪 <span
data-i18n="installation.tabs.brat">BRAT</span></button>
<button class="tab-btn" role="tab" aria-selected="false" data-tab="manual" id="tab-manual">📦
<span data-i18n="installation.tabs.manual">Manual</span></button>
</div>
<div class="tab-panels">
<div class="tab-panel is-active" role="tabpanel" aria-labelledby="tab-market" id="panel-market">
<div class="install-method">
<h3>
<span>🛒</span>
<span data-i18n="installation.marketplace.title">Community Marketplace</span>
<span class="install-status status-pending"
data-i18n="installation.marketplace.status">Pending Review</span>
</h3>
<ul class="install-steps" data-install-method="marketplace"></ul>
<div class="install-note-small" data-i18n="installation.marketplace.note">Currently in
Obsidian review queue. Until approved, use BRAT or Manual. Track PR in <code
class="repo-inline-code">obsidian-releases</code>.</div>
</div>
</div>
<div class="tab-panel" role="tabpanel" aria-labelledby="tab-brat" id="panel-brat" hidden>
<div class="install-method">
<h3>
<span>🧪</span>
<span data-i18n="installation.brat.title">BRAT (Beta)</span>
<span class="install-status status-beta"
data-i18n="installation.brat.status">Beta</span>
</h3>
<ul class="install-steps" data-install-method="brat"></ul>
<div class="install-note-small" data-i18n="installation.brat.note">Fastest way to try
latest code before Marketplace approval.</div>
</div>
</div>
<div class="tab-panel" role="tabpanel" aria-labelledby="tab-manual" id="panel-manual" hidden>
<div class="install-method">
<h3>
<span>📦</span>
<span data-i18n="installation.manual.title">Manual Install</span>
<span class="install-status status-available"
data-i18n="installation.manual.status">Advanced</span>
</h3>
<ul class="install-steps" data-install-method="manual"></ul>
<div class="install-note-small" data-i18n="installation.manual.note">Use for pinned
versions, air-gapped envs, or manual audits.</div>
</div>
</div>
</div>
</div>
<div class="install-help-text" data-i18n="installation.helpText">Need
help? See the <a href="https://github.com/sean2077/obsidian-dynamic-theme-background#readme"
target="_blank" rel="noopener noreferrer">README</a> or <a
href="https://github.com/sean2077/obsidian-dynamic-theme-background/issues" target="_blank"
rel="noopener noreferrer">report an issue</a>.</div>
</div>
</section>
<!-- Quick Start Section -->
<section class="quickstart full-section" id="quickstart">
<div class="container">
<h2 class="fade-in section-title">🚀 <span class="heading-text" data-i18n="quickstart.title">Quick
Start</span></h2>
<p class="section-subtitle fade-in" data-i18n="quickstart.subtitle">Already installed? In three short
steps you'll have smart rotation running.</p>
<div class="quickstart-content fade-in">
<div class="quickstart-steps">
<div class="quickstart-step">
<span class="step-number" data-i18n="quickstart.step1.label">STEP 1</span>
<h3 data-i18n="quickstart.step1.title">Add Sources</h3>
<p data-i18n="quickstart.step1.desc">Add local images or enable API providers (Unsplash /
Pixabay / Pexels / Wallhaven / Custom).</p>
</div>
<div class="quickstart-step">
<span class="step-number" data-i18n="quickstart.step2.label">STEP 2</span>
<h3 data-i18n="quickstart.step2.title">Set Rotation</h3>
<p data-i18n="quickstart.step2.desc">Choose interval + optional time windows (morning / day
/ night) for contextual change.</p>
</div>
<div class="quickstart-step">
<span class="step-number" data-i18n="quickstart.step3.label">STEP 3</span>
<h3 data-i18n="quickstart.step3.title">Control & Enjoy</h3>
<p data-i18n="quickstart.step3.desc">Use status bar clicks or Command Palette
(<code>dtb</code>) to switch, save & fetch.</p>
</div>
</div>
<div class="statusbar-demo">
<figure class="statusbar-figure">
<img src="assets/dtb-status-bar.jpg" alt="Status bar controls" width="800" height="450"
loading="lazy" decoding="async" />
<figcaption data-i18n="quickstart.statusbar.caption">Status Bar Snapshot</figcaption>
</figure>
<div class="statusbar-actions">
<h4 data-i18n="quickstart.statusbar.title">Status Bar Quick Control</h4>
<div class="statusbar-action">
<span class="action-ico">🖱️</span>
<span data-i18n="quickstart.statusbar.leftClick"><strong>Left Click</strong> Random
wallpaper</span>
</div>
<div class="statusbar-action">
<span class="action-ico">🖱️</span>
<span data-i18n="quickstart.statusbar.middleClick"><strong>Middle Click</strong> Open
settings tab</span>
</div>
<div class="statusbar-action">
<span class="action-ico">🖱️</span>
<span data-i18n="quickstart.statusbar.rightClick"><strong>Right Click</strong> Save
current background</span>
</div>
<p class="statusbar-hint" data-i18n="quickstart.statusbar.hint">Daily interaction happens
here or via the command palette.</p>
<p class="statusbar-tip" data-i18n="quickstart.statusbar.tip">Tip: assign hotkeys for even
faster switching</p>
</div>
</div>
</div>
</div>
</section>
<!-- Sources Section -->
<section class="api-sources full-section" id="sources">
<div class="container">
<h2 class="fade-in section-title">🌐 <span class="heading-text" data-i18n="sources.title">Supported
Sources</span></h2>
<p class="fade-in sources-subtitle" data-i18n="sources.subtitle">High quality
wallpaper platforms + custom endpoints</p>
<div class="api-logos">
<a class="api-logo fade-in" style="--accent:#9aa0ae;" href="https://unsplash.com/" target="_blank"
rel="noopener noreferrer">
<div>📸</div>
<div class="api-logo-name">Unsplash</div>
<p data-i18n="sources.unsplash">Photography</p>
</a>
<a class="api-logo fade-in" style="--accent:#28c76f;" href="https://pixabay.com/" target="_blank"
rel="noopener noreferrer">
<div>🖼️</div>
<div class="api-logo-name">Pixabay</div>
<p data-i18n="sources.pixabay">Free images</p>
</a>
<a class="api-logo fade-in" style="--accent:#14b8a6;" href="https://www.pexels.com/" target="_blank"
rel="noopener noreferrer">
<div>🎨</div>
<div class="api-logo-name">Pexels</div>
<p data-i18n="sources.pexels">Curated shots</p>
</a>
<a class="api-logo fade-in" style="--accent:#8b5cf6;" href="https://wallhaven.cc/" target="_blank"
rel="noopener noreferrer">
<div>🏞️</div>
<div class="api-logo-name">Wallhaven</div>
<p data-i18n="sources.wallhaven">Wallpaper hub</p>
</a>
<a class="api-logo fade-in" style="--accent:#00d26a;" href="https://wallpaper.apc.360.cn/"
target="_blank" rel="noopener noreferrer">
<div>🎯</div>
<div class="api-logo-name">360 Wallpaper</div>
<p data-i18n="sources.qihoo360">HD categories</p>
</a>
<button type="button" class="api-logo fade-in" style="--accent:#ff9800;" id="custom-api-btn">
<div>🔧</div>
<div class="api-logo-name">Custom API</div>
<p data-i18n="sources.custom">Bring your own</p>
</button>
</div>
</div>
</section>
<!-- FAQ Section -->
<section class="faq full-section" id="faq">
<div class="container">
<h2 class="fade-in section-title"><span class="heading-text" data-i18n="faq.title">FAQ</span></h2>
<div class="faq-grid">
<div class="faq-item fade-in">
<div class="faq-question">Which wallpaper sources are supported?</div>
<div class="faq-answer">Unsplash, Pixabay, Pexels, Wallhaven plus custom APIs; more incoming.
</div>
</div>
<div class="faq-item fade-in">
<div class="faq-question">How do I set rotation rules?</div>
<div class="faq-answer">Configure time slots, interval auto-rotate or trigger manually via the
command palette.</div>
</div>
<div class="faq-item fade-in">
<div class="faq-question">Theme & mobile support?</div>
<div class="faq-answer">Yes—responsive layout and theme variable integration for dark & light
modes.</div>
</div>
<div class="faq-item fade-in">
<div class="faq-question">Performance impact?</div>
<div class="faq-answer">Async loading + smart caching keeps the workspace responsive.</div>
</div>
<div class="faq-item fade-in">
<div class="faq-question">Feedback & issues?</div>
<div class="faq-answer">Report bugs via <a
href="https://github.com/sean2077/obsidian-dynamic-theme-background/issues/new?assignees=&labels=&projects=&template=bug_report.md&title="
target="_blank" rel="noopener noreferrer">Bug Report</a> or suggest features via <a
href="https://github.com/sean2077/obsidian-dynamic-theme-background/issues/new?assignees=&labels=&projects=&template=feature_request.md&title="
target="_blank" rel="noopener noreferrer">Feature Request</a>.</div>
</div>
<div class="faq-item fade-in">
<div class="faq-question">Where is configuration stored?</div>
<div class="faq-answer">Inside your vault
<code>.obsidian/plugins/obsidian-dynamic-theme-background/</code> folder (standard Obsidian
plugin pattern).
</div>
</div>
</div>
<div class="faq-disclaimer fade-in" data-i18n="faq.disclaimer">API
usage must comply with each provider's Terms of Service. Respect rate limits & attribution policies
where required.</div>
</div>
</section>
</main>
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h3 data-i18n="footer.aboutTitle">About DTB</h3>
<div data-i18n="footer.aboutDesc">Dynamic Theme Background enhances Obsidian with dynamic, smart
wallpapers for inspiration.</div>
</div>
<div class="footer-section">
<h3 data-i18n="footer.linksTitle">Links</h3>
<div class="footer-links">
<a href="https://github.com/sean2077/obsidian-dynamic-theme-background" target="_blank"
rel="noopener noreferrer" data-i18n="footer.github">GitHub Repo</a>
<a href="https://github.com/sean2077/obsidian-dynamic-theme-background#readme" target="_blank"
rel="noopener noreferrer" data-i18n="footer.docs">Documentation</a>
<a href="#faq" data-i18n="footer.faq">FAQ</a>
</div>
</div>
<div class="footer-section">
<h3 data-i18n="footer.contactTitle">Contact</h3>
<div data-i18n="footer.contactDesc">Need help or custom work? Contact via GitHub Issues.</div>
</div>
</div>
<div class="badges">
<span class="badge" data-i18n="footer.license">MIT License</span>
<span class="badge" data-i18n="footer.madeBy">Made with ❤️ by sean2077</span>
<span class="badge" data-i18n="footer.community">Obsidian Community</span>
<a class="badge" id="dtb-version-footer" data-fallback="v?"
href="https://github.com/sean2077/obsidian-dynamic-theme-background/releases" target="_blank"
rel="noopener noreferrer">version…</a>
</div>
<div class="footer-copyright"><span data-i18n="footer.copyright">© 2025 Dynamic Theme
Background. All rights reserved.</span></div>
</div>
</footer>
<!-- Fixed Bottom Right Buttons -->
<div class="fixed-buttons">
<button id="back-to-top" data-i18n-aria-label="nav.backToTop" data-i18n-title="nav.backToTop">
<span class="back-icon">
<span class="back-arrow"></span>
<span class="back-line"></span>
</span>
</button>
<div class="dtb-btn-wrapper">
<div class="dtb-arrow-hint" id="dtb-arrow-hint">
<span class="arrow-text" data-i18n="hero.trySwitch">Try switching →</span>
</div>
<button id="dtb-switch-btn" title="Click to switch background" aria-label="Switch background">
<span class="dtb-icon">🌈</span>
</button>
</div>
</div>
<script>
(function () {
// Time-based dynamic background
(function () {
var bgEl = document.querySelector('.dynamic-bg');
if (!bgEl) return;
var timeClasses = ['time-dawn', 'time-morning', 'time-noon', 'time-afternoon', 'time-dusk', 'time-night'];
var timeLabels = ['🌅 Dawn', '☀️ Morning', '🌞 Noon', '🌤️ Afternoon', '🌇 Dusk', '🌙 Night'];
var currentIndex = 0;
// Demo mode: cycle through all times every 10 seconds
var demoMode = new URLSearchParams(location.search).has('demo');
function getTimeClass() {
var hour = new Date().getHours();
if (hour >= 5 && hour < 8) return 'time-dawn';
if (hour >= 8 && hour < 11) return 'time-morning';
if (hour >= 11 && hour < 14) return 'time-noon';
if (hour >= 14 && hour < 17) return 'time-afternoon';
if (hour >= 17 && hour < 19) return 'time-dusk';
return 'time-night';
}
function getTimeIndex() {
var cls = getTimeClass();
return timeClasses.indexOf(cls);
}
function updateBackground(index) {
if (typeof index === 'number') {
currentIndex = index;
}
var cls = timeClasses[currentIndex];
bgEl.className = 'dynamic-bg ' + cls;
// Update DTB button tooltip
var dtbBtn = document.getElementById('dtb-switch-btn');
if (dtbBtn) {
dtbBtn.title = timeLabels[currentIndex] + ' - Click to switch';
}
}
// Initialize with current time
currentIndex = getTimeIndex();
updateBackground();
// DTB button click handler
var dtbBtn = document.getElementById('dtb-switch-btn');
var arrowHint = document.getElementById('dtb-arrow-hint');
if (dtbBtn) {
dtbBtn.addEventListener('click', function () {
currentIndex = (currentIndex + 1) % timeClasses.length;
updateBackground();
// Add click animation
dtbBtn.classList.add('clicked');
setTimeout(function () { dtbBtn.classList.remove('clicked'); }, 200);
// Hide arrow hint after first click
if (arrowHint) {
arrowHint.classList.add('hidden');
}
});
}
// Auto-hide arrow hint after 8 seconds
if (arrowHint) {
setTimeout(function () {
arrowHint.classList.add('hidden');
}, 10000);
}
if (demoMode) {
console.log('🎬 Demo mode enabled - backgrounds will cycle every 10 seconds');
setInterval(function () {
currentIndex = (currentIndex + 1) % timeClasses.length;
updateBackground();
}, 10000);
} else {
// Production: update every minute based on real time
setInterval(function () {
currentIndex = getTimeIndex();
updateBackground();
}, 60000);
}
})();
// Skip particles on low-perf mode
if (document.documentElement.classList.contains('dtb-perf-low')) return;
// Lazy particles - reduced count for better performance
var particles = document.querySelector('.floating-particles');
var particleCount = 10;
var particlesSpawned = false;
function spawnParticles() {
if (particlesSpawned || !particles) return;
particlesSpawned = true;
for (var i = 0; i < particleCount; i++) {
var p = document.createElement('div');
p.className = 'particle';
var s = Math.random() * 28 + 10;
p.style.cssText = 'width:' + s + 'px;height:' + s + 'px;left:' + (Math.random() * 100) + '%;top:' + (Math.random() * 100) + '%;animation-duration:' + (Math.random() * 18 + 14) + 's;background:rgba(255,255,255,' + (Math.random() * 0.1 + 0.03) + ')';
particles.appendChild(p);
}
}
window.addEventListener('scroll', spawnParticles, { once: true, passive: true });
setTimeout(spawnParticles, 1500);
// IntersectionObserver fade-in (skip on iOS/low-perf for immediate visibility)
var skipFadeIn = document.documentElement.classList.contains('dtb-ios') ||
document.documentElement.classList.contains('dtb-perf-low');
var io = (!skipFadeIn && 'IntersectionObserver' in window) ? new IntersectionObserver(function (es) {
es.forEach(function (e) {
if (e.isIntersecting) { e.target.classList.add('visible'); io.unobserve(e.target); }
});
}, { rootMargin: '0px 0px -80px 0px', threshold: 0.1 }) : null;
document.querySelectorAll('.fade-in').forEach(function (el) {
if (io) io.observe(el); else el.classList.add('visible');
});
// Smooth anchors
document.querySelectorAll('a[href^="#"]').forEach(function (a) {
a.addEventListener('click', function (e) {
if (a.classList.contains('logo')) {
e.preventDefault();
window.scrollTo({ top: 0, behavior: 'smooth' });
return;
}
var id = a.getAttribute('href').slice(1);
var t = document.getElementById(id);
if (t) { e.preventDefault(); t.scrollIntoView({ behavior: 'smooth', block: 'start' }); }
});
});
// Back to top
var back = document.getElementById('back-to-top');
function threshold() { return window.innerHeight * 0.65; }
function toggleBack() { back.classList.toggle('visible', window.scrollY > threshold()); }
window.addEventListener('scroll', toggleBack, { passive: true });
toggleBack();
back.addEventListener('click', function () { window.scrollTo({ top: 0, behavior: 'smooth' }); });
// Dynamic version fetch
(function () {
var headerEl = document.getElementById('dtb-version-header');
var footerEl = document.getElementById('dtb-version-footer');
function setVersion(v, url) {
if (headerEl) { headerEl.textContent = v; headerEl.dataset.releaseUrl = url || ''; headerEl.style.cursor = 'pointer'; headerEl.title = 'Open release ' + v; }
if (footerEl) { footerEl.textContent = v; footerEl.dataset.releaseUrl = url || ''; footerEl.title = 'Open release ' + v; if (footerEl.tagName === 'A' && url) footerEl.setAttribute('href', url); }
}
var params = new URLSearchParams(location.search);
var skipApi = params.has('noapi') || params.get('noapi') === '1';
fetch('https://api.github.com/repos/sean2077/obsidian-dynamic-theme-background/releases/latest', {
headers: { 'Accept': 'application/vnd.github+json' }
}).then(function (res) {
if (skipApi || !res.ok) throw new Error('skip');
return res.json();
}).then(function (json) {
var tag = json.tag_name || json.name || '';
if (tag && !/^v/i.test(tag)) tag = 'v' + tag;
if (!tag) tag = headerEl?.dataset.fallback || 'v?';
var releaseUrl = 'https://github.com/sean2077/obsidian-dynamic-theme-background/releases/tag/' + tag.replace(/^v/i, '');
setVersion(tag, releaseUrl);
// Update JSON-LD
var schema = document.getElementById('schema-dtb');
var ver = tag.replace(/^v/, '');
if (schema) {
try {
var data = JSON.parse(schema.textContent || '{}');
data.softwareVersion = ver;
schema.textContent = JSON.stringify(data);
} catch (err) { }
}
}).catch(function () {
var fallback = headerEl?.dataset.fallback || 'v?';
var fallbackUrl = 'https://github.com/sean2077/obsidian-dynamic-theme-background/releases';
setVersion(fallback, fallbackUrl);
if (footerEl && footerEl.tagName === 'A') footerEl.setAttribute('href', fallbackUrl);
});
if (headerEl) {
headerEl.addEventListener('click', function (e) {
var u = headerEl.dataset.releaseUrl;
if (u) { e.preventDefault(); e.stopPropagation(); location.assign(u); }
});
}
})();
// Installation tabs
(function () {
var container = document.getElementById('install-tabs');
if (!container) return;
var buttons = Array.from(container.querySelectorAll('.tab-btn'));
var panels = Array.from(container.querySelectorAll('.tab-panel'));
buttons.forEach(function (b, i) {
var id = b.dataset.tab;
b.id = b.id || ('tab-' + id);
var pnl = container.querySelector('#panel-' + id);
if (pnl) pnl.setAttribute('aria-labelledby', b.id);
b.setAttribute('tabindex', i === 0 ? '0' : '-1');
});
function activate(id, focus, updateHash) {
buttons.forEach(function (b) {
var sel = b.dataset.tab === id;
b.classList.toggle('is-active', sel);
b.setAttribute('aria-selected', sel ? 'true' : 'false');
b.setAttribute('tabindex', sel ? '0' : '-1');
if (sel && focus) b.focus();
});
panels.forEach(function (p) {
var show = p.id === 'panel-' + id;
p.classList.toggle('is-active', show);
if (show) p.removeAttribute('hidden'); else p.setAttribute('hidden', '');
});
if (updateHash) history.replaceState(null, '', '#install-' + id);
}
buttons.forEach(function (btn) {
btn.addEventListener('click', function () { activate(btn.dataset.tab, true, true); });
});
container.addEventListener('keydown', function (e) {
var current = buttons.findIndex(function (b) { return b.getAttribute('aria-selected') === 'true'; });
if (['ArrowRight', 'ArrowLeft', 'Home', 'End'].indexOf(e.key) >= 0) {
e.preventDefault();
var ni = current;
if (e.key === 'ArrowRight') ni = (current + 1) % buttons.length;
else if (e.key === 'ArrowLeft') ni = (current - 1 + buttons.length) % buttons.length;
else if (e.key === 'Home') ni = 0;
else if (e.key === 'End') ni = buttons.length - 1;
activate(buttons[ni].dataset.tab, true, true);
}
});
var match = location.hash.match(/^#install-(market|brat|manual)$/);
if (match) activate(match[1], false, false); else activate(buttons[0].dataset.tab, false, false);
})();
})();
</script>
<!-- i18n support -->
<script src="i18n.js" defer></script>
</body>
</html>