feat: full site

This commit is contained in:
2025-09-21 23:31:15 +03:00
parent 6de65bca11
commit c63ef78b03
38 changed files with 1541 additions and 1481 deletions

View File

@@ -0,0 +1,10 @@
<div class="install-cmd relative flex items-center gap-<%= size == 'sm' ? '2' : '4' %> rounded-2xl border border-slate-200 bg-slate-900 shadow-sm dark:border-slate-700 dark:bg-slate-950">
<input
type="text"
readonly
value="<%= h(command) %>"
onclick="this.select()"
class="flex-1 bg-transparent <%= size == 'sm' ? 'py-2 text-xs' : 'py-3 text-sm' %> font-mono text-slate-100 outline-none cursor-pointer border-0 appearance-none focus:outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-400 dark:focus-visible:outline-sky-300 focus-visible:outline-offset-[-2px] rounded-2xl px-8"
>
<span class="<%= size == 'sm' ? 'text-[10px]' : 'text-xs' %> uppercase tracking-wide text-slate-400 pr-8"><%= h(label || 'CLI') %></span>
</div>

7
theme/_footer.html.erb Normal file
View File

@@ -0,0 +1,7 @@
<footer class="border-t border-slate-200 pt-6 text-sm text-slate-500 dark:border-slate-700 dark:text-slate-400">
<p class="px-8">
<time datetime="<%= h(generated_at) %>" title="<%= h(generated_at) %>">
Generated at <%= h(format_relative_time(generated_at)) %>
</time>
</p>
</footer>

View File

@@ -0,0 +1,55 @@
<%
# Required parameters:
# - formula: The formula hash object
# - base_path: Path prefix for formula links (e.g., '' or '../')
# Optional parameters:
# - show_homepage: Show homepage link (default: false)
# - show_dependencies: Show dependency badges (default: false)
# - show_last_modified: Show last modified time (default: false)
formula_name = formula['name'].to_s
return if formula_name.empty?
formula_slug = formula_name.gsub(/[^a-z0-9._-]/i, '-')
formula_slug = formula_name if formula_slug.empty?
formula_description = formula['description'].to_s
# Check if methods exist (they're defined as singleton methods in PartialContext)
show_homepage = respond_to?(:show_homepage) ? self.show_homepage : false
show_dependencies = respond_to?(:show_dependencies) ? self.show_dependencies : false
show_last_modified = respond_to?(:show_last_modified) ? self.show_last_modified : false
%>
<article class="formula-card flex h-full flex-col gap-4 rounded-3xl border border-slate-200 bg-white/80 p-6 shadow-sm ring-1 ring-black/5 transition hover:-translate-y-1 hover:shadow-md dark:border-slate-700 dark:bg-slate-900/70 dark:ring-white/10">
<header class="space-y-2">
<h3 class="text-xl font-semibold">
<a href="<%= base_path %>formula/<%= h(formula_slug) %>.html" class="transition hover:text-sky-600 dark:hover:text-sky-400"><%= h(formula_name) %></a>
</h3>
<p class="text-sm text-slate-600 dark:text-slate-300"><%= h(formula_description) %></p>
<% if show_homepage && formula['homepage'] %>
<p class="text-sm">
<a href="<%= h(formula['homepage'].to_s) %>" target="_blank" class="inline-flex items-center gap-1 text-sky-600 transition hover:text-sky-500 dark:text-sky-400 dark:hover:text-sky-300">
Homepage
<span aria-hidden="true">↗</span>
</a>
</p>
<% end %>
</header>
<% if show_last_modified && formula['last_modified'] %>
<div class="flex items-center gap-2 text-xs text-slate-500 dark:text-slate-400">
<span>Updated <%= format_relative_time(formula['last_modified']) %></span>
</div>
<% end %>
<div class="flex flex-wrap gap-2">
<% if formula['license'] %>
<span class="badge inline-flex items-center rounded-full bg-sky-500 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-white dark:bg-sky-400">License: <%= h(formula['license'].to_s) %></span>
<% end %>
<% if formula['version'] %>
<span class="badge inline-flex items-center rounded-full bg-emerald-500 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-white dark:bg-emerald-400">v<%= h(formula['version'].to_s) %></span>
<% end %>
<% if show_dependencies %>
<% Array(formula['dependencies']).compact.each do |dep| %>
<span class="badge inline-flex items-center rounded-full bg-slate-800 px-3 py-1 text-xs uppercase tracking-wide text-white dark:bg-slate-700">dep: <%= h(dep.to_s) %></span>
<% end %>
<% end %>
</div>
</article>

31
theme/_head.html.erb Normal file
View File

@@ -0,0 +1,31 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<% page_title = title.to_s %>
<title><%= h(page_title) %></title>
<% stylesheet = style_path || 'style.css' %>
<link rel="stylesheet" href="<%= h(stylesheet) %>">
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
<script>
tailwind.config = {
darkMode: "class"
};
</script>
<script>
(function () {
if (typeof window === "undefined") return;
try {
const stored = window.localStorage.getItem("theme");
const prefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
const theme = stored || (prefersDark ? "dark" : "light");
document.documentElement.classList.toggle("dark", theme === "dark");
document.documentElement.classList.toggle("light", theme !== "dark");
document.documentElement.setAttribute("data-theme", theme);
} catch (error) {
document.documentElement.classList.toggle("dark", false);
document.documentElement.classList.toggle("light", true);
document.documentElement.setAttribute("data-theme", "light");
}
})();
</script>
</head>

15
theme/_header.html.erb Normal file
View File

@@ -0,0 +1,15 @@
<header class="flex flex-col gap-6">
<nav aria-label="Main navigation" class="px-8">
<%= render_partial('nav', active: active_page, base_path: base_path) %>
</nav>
<div class="header flex flex-col gap-6 rounded-3xl border border-slate-200 bg-white/80 p-8 shadow-sm ring-1 ring-black/5 backdrop-blur dark:border-slate-700 dark:bg-slate-900/80 dark:ring-white/10">
<div class="flex items-start justify-between gap-4">
<div class="space-y-2">
<h1 class="text-3xl font-bold tracking-tight md:text-4xl"><%= h(tap_name) %></h1>
<p class="max-w-3xl text-base text-slate-600 dark:text-slate-300"><%= h(description) %></p>
</div>
<button class="theme-toggle inline-flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full border border-slate-300 bg-white/80 text-xl text-slate-600 shadow-sm transition hover:bg-slate-100 hover:text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500 dark:border-slate-600 dark:bg-slate-900/70 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white" aria-label="Toggle dark mode" tabindex="0">🌙</button>
</div>
</div>
</header>

13
theme/_nav.html.erb Normal file
View File

@@ -0,0 +1,13 @@
<% nav_base = "rounded-full px-3 py-1 transition hover:bg-slate-100 hover:text-slate-900 dark:hover:bg-slate-800 dark:hover:text-white" %>
<% nav_active = "bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-900" %>
<% active_tab = (active || :none).to_sym rescue :none %>
<% prefix = base_path.to_s %>
<% href = lambda do |page|
prefix.empty? ? page : File.join(prefix, page)
end %>
<nav aria-label="Main navigation" class="flex flex-wrap items-center gap-3 text-sm font-medium text-slate-600 dark:text-slate-300">
<% home_classes = [nav_base, (active_tab == :home ? nav_active : nil)].compact.join(' ') %>
<% formulae_classes = [nav_base, (active_tab == :formulae ? nav_active : nil)].compact.join(' ') %>
<a href="<%= h(href.call('index.html')) %>" class="<%= home_classes %>">Home</a>
<a href="<%= h(href.call('formulae.html')) %>" class="<%= formulae_classes %>">All Formulae</a>
</nav>

View File

@@ -0,0 +1,6 @@
<main class="flex flex-1 flex-col gap-8">
<div class="rounded-2xl border border-slate-200 bg-white/80 p-8 text-center shadow-sm ring-1 ring-black/5 backdrop-blur dark:border-slate-700 dark:bg-slate-900/80 dark:ring-white/10">
<h2 class="text-2xl font-semibold tracking-tight mb-4">No formulae available</h2>
<p class="text-slate-600 dark:text-slate-300">This tap currently has no formulae. Please check back later.</p>
</div>
</main>

Binary file not shown.

Binary file not shown.

111
theme/formula.html.erb Normal file
View File

@@ -0,0 +1,111 @@
<!DOCTYPE html>
<html lang="en" class="h-full min-h-full scroll-smooth font-sans transition-colors duration-200">
<% raw_tap_name = @data['tap_name'].to_s %>
<% tap_name = raw_tap_name.empty? ? 'ivuorinen/tap' : raw_tap_name %>
<% formula_name = @formula['name'].to_s %>
<% name = h("#{tap_name}/#{formula_name}") %>
<%= render_partial('head', title: "#{formula_name} - #{tap_name}", style_path: '../style.css') %>
<body class="min-h-screen bg-slate-50 text-slate-900 transition-colors duration-200 dark:bg-slate-950 dark:text-slate-100">
<div class="mx-auto flex min-h-screen max-w-6xl flex-col gap-12 px-6 py-10">
<%= render_partial('header', tap_name: name, description: @formula['description'].to_s, active_page: :formulae, base_path: '../') %>
<main class="flex flex-1 flex-col gap-8">
<section class="space-y-4">
<div class="flex items-center justify-between px-8 pr-6">
<h2 class="text-2xl font-semibold tracking-tight h-10">Installation</h2>
<div class="flex gap-2">
<% if @formula['url'] %>
<a href="<%= h(@formula['url'].to_s) %>" target="_blank" class="inline-flex items-center justify-center rounded-full border border-slate-200 p-2 text-slate-600 transition hover:border-slate-300 hover:bg-slate-50 hover:text-slate-900 dark:border-slate-700 dark:text-slate-400 dark:hover:border-slate-600 dark:hover:bg-slate-800 dark:hover:text-white" aria-label="Download source">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
</a>
<% end %>
<% if @formula['homepage'] %>
<a href="<%= h(@formula['homepage'].to_s) %>" target="_blank" class="inline-flex items-center justify-center rounded-full border border-slate-200 p-2 text-slate-600 transition hover:border-slate-300 hover:bg-slate-50 hover:text-slate-900 dark:border-slate-700 dark:text-slate-400 dark:hover:border-slate-600 dark:hover:bg-slate-800 dark:hover:text-white" aria-label="View on GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
</a>
<% end %>
</div>
</div>
<%= render_partial('command_input', command: "brew install #{name}", size: 'lg', label: 'CLI') %>
</section>
<section>
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white/80 shadow-sm ring-1 ring-black/5 dark:border-slate-700 dark:bg-slate-900/70 dark:ring-white/10">
<table class="w-full divide-y divide-slate-200 text-sm dark:divide-slate-700">
<caption class="sr-only">Formula Details</caption>
<tbody>
<% if @formula['version'] %>
<tr class="divide-x divide-slate-200 dark:divide-slate-700">
<th scope="row" class="w-40 bg-slate-50 px-4 py-3 text-left font-medium text-slate-700 dark:bg-slate-900 dark:text-slate-300">Version</th>
<td class="px-4 py-3 text-slate-600 dark:text-slate-200"><%= h(@formula['version'].to_s) %></td>
</tr>
<% end %>
<% if @formula['license'] %>
<tr class="divide-x divide-slate-200 dark:divide-slate-700">
<th scope="row" class="w-40 bg-slate-50 px-4 py-3 text-left font-medium text-slate-700 dark:bg-slate-900 dark:text-slate-300">License</th>
<td class="px-4 py-3 text-slate-600 dark:text-slate-200"><%= h(@formula['license'].to_s) %></td>
</tr>
<% end %>
<% if @formula['homepage'] %>
<tr class="divide-x divide-slate-200 dark:divide-slate-700">
<th scope="row" class="w-40 bg-slate-50 px-4 py-3 text-left font-medium text-slate-700 dark:bg-slate-900 dark:text-slate-300">Homepage</th>
<td class="px-4 py-3">
<a href="<%= h(@formula['homepage'].to_s) %>" target="_blank" class="inline-flex items-center gap-1 text-sky-600 transition hover:text-sky-500 dark:text-sky-400 dark:hover:text-sky-300">
<%= h(@formula['homepage'].to_s) %>
<span aria-hidden="true">↗</span>
</a>
</td>
</tr>
<% end %>
<% dependencies = Array(@formula['dependencies']).reject(&:nil?) %>
<% if dependencies.any? %>
<tr class="divide-x divide-slate-200 dark:divide-slate-700">
<th scope="row" class="w-40 bg-slate-50 px-4 py-3 text-left font-medium text-slate-700 dark:bg-slate-900 dark:text-slate-300">Dependencies</th>
<td class="px-4 py-3">
<div class="flex flex-wrap gap-2">
<% dependencies.each do |dep| %>
<span class="inline-flex items-center rounded-full bg-slate-800 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-white dark:bg-slate-700">dep: <%= h(dep.to_s) %></span>
<% end %>
</div>
</td>
</tr>
<% end %>
<tr class="divide-x divide-slate-200 dark:divide-slate-700">
<th scope="row" class="w-40 bg-slate-50 px-4 py-3 text-left font-medium text-slate-700 dark:bg-slate-900 dark:text-slate-300">Formula File</th>
<td class="px-4 py-3"><code class="rounded-xl bg-slate-900 px-3 py-2 font-mono text-xs text-slate-100 dark:bg-slate-950"><%= h(@formula['file_path'].to_s) %></code></td>
</tr>
<tr class="divide-x divide-slate-200 dark:divide-slate-700">
<th scope="row" class="w-40 bg-slate-50 px-4 py-3 text-left font-medium text-slate-700 dark:bg-slate-900 dark:text-slate-300">Last Modified</th>
<td class="px-4 py-3 text-slate-600 dark:text-slate-200"><%= h(@formula['last_modified'].to_s) %></td>
</tr>
<% if @formula['sha256'] %>
<tr class="divide-x divide-slate-200 dark:divide-slate-700">
<th scope="row" class="w-40 bg-slate-50 px-4 py-3 text-left font-medium text-slate-700 dark:bg-slate-900 dark:text-slate-300">SHA256</th>
<td class="px-4 py-3">
<input
type="text"
readonly
value="<%= h(@formula['sha256'].to_s) %>"
onclick="this.select()"
class="w-full bg-slate-50 dark:bg-slate-800 px-3 py-1.5 font-mono text-xs text-slate-800 dark:text-slate-200 rounded-lg border border-slate-200 dark:border-slate-600 cursor-pointer appearance-none focus:outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-400 dark:focus-visible:outline-sky-300 focus-visible:outline-offset-[-2px]"
>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</section>
</main>
</div>
<%= render_partial('footer', generated_at: @data['generated_at']) %>
<script src="../main.js"></script>
</body>
</html>

46
theme/formulae.html.erb Normal file
View File

@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en" class="h-full min-h-full scroll-smooth font-sans transition-colors duration-200">
<% raw_tap_name = @data['tap_name'].to_s %>
<% tap_name = raw_tap_name.empty? ? 'ivuorinen/tap' : raw_tap_name %>
<%= render_partial('head', title: "All Formulae - #{tap_name}", style_path: 'style.css') %>
<body class="min-h-screen bg-slate-50 text-slate-900 transition-colors duration-200 dark:bg-slate-950 dark:text-slate-100">
<div class="mx-auto flex min-h-screen max-w-6xl flex-col gap-12 px-6 py-10">
<%= render_partial('header', tap_name: tap_name, description: 'Homebrew Tap containing custom formulae for various tools and utilities.', active_page: :formulae, base_path: '') %>
<% if @data['formulae_count'].to_i == 0 %>
<%= render_partial('nothing_here') %>
<% else %>
<main class="flex flex-1 flex-col gap-8">
<section class="space-y-6">
<div class="flex items-end justify-between gap-4">
<h2 class="text-2xl font-semibold tracking-tight px-8">All Formulae</h2>
<p class="text-sm text-slate-500 dark:text-slate-400 px-8"><%= h(@data['formulae_count'].to_s) %> total</p>
</div>
<div class="mb-4">
<input
type="search"
id="formula-search"
placeholder="Search formulae..."
class="w-full rounded-xl border border-slate-200 bg-white px-8 py-2 text-sm placeholder-slate-400 shadow-sm transition focus:border-sky-400 focus:outline-none focus:ring-1 focus:ring-sky-400 dark:border-slate-700 dark:bg-slate-900 dark:placeholder-slate-500 dark:focus:border-sky-300 dark:focus:ring-sky-300"
>
</div>
<div class="formula-grid grid gap-6 md:grid-cols-2 xl:grid-cols-3">
<%
# Sort all formulae alphabetically by name
@data['formulae'].sort_by { |f| f['name'].to_s.downcase }.each do |formula|
%>
<%= render_partial('formula_card', formula: formula, base_path: '', show_homepage: true, show_dependencies: true, show_last_modified: true) %>
<% end %>
</div>
</section>
</main>
<% end %>
<%= render_partial('footer', generated_at: @data['generated_at']) %>
</div>
<script src="main.js"></script>
</body>
</html>

49
theme/index.html.erb Normal file
View File

@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en" class="h-full min-h-full scroll-smooth font-sans transition-colors duration-200">
<% raw_tap_name = @data['tap_name'].to_s %>
<% safe_tap_name = raw_tap_name.empty? ? 'ivuorinen/homebrew-tap' : raw_tap_name %>
<%= render_partial('head', title: safe_tap_name, style_path: 'style.css') %>
<body class="min-h-screen bg-slate-50 text-slate-900 transition-colors duration-200 dark:bg-slate-950 dark:text-slate-100">
<div class="mx-auto flex min-h-screen max-w-6xl flex-col gap-12 px-6 py-10">
<%= render_partial('header', tap_name: safe_tap_name, description: 'Homebrew Tap containing custom formulae for various tools and utilities.', active_page: :home, base_path: '') %>
<% if @data['formulae_count'].to_i == 0 %>
<%= render_partial('nothing_here') %>
<% else %>
<main class="flex flex-1 flex-col gap-8">
<section class="space-y-4">
<h2 class="text-2xl font-semibold tracking-tight px-8 h-10">Quick Start</h2>
<%= render_partial('command_input', command: "brew tap #{safe_tap_name}", size: 'lg', label: 'CLI') %>
</section>
<section class="space-y-6">
<div class="flex items-end justify-between gap-4">
<h2 class="text-2xl font-semibold tracking-tight px-8">Latest Updates</h2>
<a href="formulae.html" class="text-sm text-sky-600 hover:text-sky-500 dark:text-sky-400 dark:hover:text-sky-300 px-8">View all <%= h(@data['formulae_count'].to_s) %> formulae →</a>
</div>
<div class="formula-grid grid gap-6 md:grid-cols-2 xl:grid-cols-3">
<%
# Sort formulae by last_modified date (most recent first) and take top 6
sorted_formulae = Array(@data['formulae'])
.select { |f| f.is_a?(Hash) && f['last_modified'] }
.sort_by { |f| f['last_modified'] }
.reverse
.first(6)
sorted_formulae.each do |formula|
%>
<%= render_partial('formula_card', formula: formula, base_path: '', show_last_modified: true) %>
<% end %>
</div>
</section>
</main>
<% end %>
<%= render_partial('footer', generated_at: @data['generated_at'].to_s) %>
</div>
<script src="main.js"></script>
</body>
</html>

156
theme/main.js Normal file
View File

@@ -0,0 +1,156 @@
// Dark mode toggle functionality
(() => {
const STORAGE_KEY = "theme";
function getStoredTheme() {
try {
return localStorage.getItem(STORAGE_KEY);
} catch {
return null;
}
}
function setStoredTheme(theme) {
try {
localStorage.setItem(STORAGE_KEY, theme);
} catch {
// Ignore storage failures
}
}
function getSystemTheme() {
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
function getCurrentTheme() {
return getStoredTheme() || getSystemTheme() || "light";
}
function applyTheme(theme) {
document.documentElement.classList.toggle("dark", theme === "dark");
const toggle = document.querySelector(".theme-toggle");
if (toggle) {
toggle.innerHTML = theme === "dark" ? "☀️" : "🌙";
toggle.setAttribute(
"aria-label",
theme === "dark" ? "Switch to light mode" : "Switch to dark mode",
);
}
}
function toggleTheme() {
const currentTheme = getCurrentTheme();
const newTheme = currentTheme === "dark" ? "light" : "dark";
setStoredTheme(newTheme);
applyTheme(newTheme);
}
// Watch for system theme changes
const mediaQuery = window.matchMedia?.("(prefers-color-scheme: dark)");
mediaQuery?.addEventListener("change", (e) => {
if (!getStoredTheme()) {
applyTheme(e.matches ? "dark" : "light");
}
});
// Theme toggle click handler
document.addEventListener("click", (e) => {
if (e.target.closest(".theme-toggle")) {
e.preventDefault();
toggleTheme();
}
});
// Initialize theme
applyTheme(getCurrentTheme());
window.toggleTheme = toggleTheme;
})();
// Click-to-copy functionality for command inputs
(() => {
async function copyToClipboard(input) {
input.select();
input.setSelectionRange(0, 99999);
try {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(input.value);
return true;
}
} catch {
// Keep text selected for manual copy
}
return false;
}
function showCopyFeedback(element, success) {
if (!success) return;
const label = element.closest(".install-cmd")?.querySelector("span");
if (!label) return;
const originalText = label.textContent;
label.textContent = "Copied!";
label.style.color = "#10b981";
setTimeout(() => {
label.textContent = originalText;
label.style.color = "";
}, 1500);
}
// Setup copy handlers using event delegation
document.addEventListener("click", async (e) => {
const input = e.target.closest('input[readonly][onclick*="select"]');
if (input) {
input.removeAttribute("onclick");
const success = await copyToClipboard(input);
showCopyFeedback(input, success);
}
});
})();
// Formula search functionality
(() => {
let searchTimeout = null;
const searchInput = document.getElementById("formula-search");
if (!searchInput) return;
function fuzzyMatch(needle, haystack) {
if (!needle) return true;
needle = needle.toLowerCase();
haystack = haystack.toLowerCase();
let j = 0;
for (let i = 0; i < needle.length; i++) {
const char = needle[i];
j = haystack.indexOf(char, j);
if (j === -1) return false;
j++;
}
return true;
}
function performSearch() {
const searchTerm = searchInput.value.trim();
const cards = document.querySelectorAll(".formula-card");
cards.forEach((card) => {
const text = `${card.querySelector("h3")?.textContent || ""} ${card.querySelector("p")?.textContent || ""}`;
card.style.display = !searchTerm || fuzzyMatch(searchTerm, text) ? "" : "none";
});
}
searchInput.addEventListener("input", () => {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(performSearch, 300);
});
searchInput.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
e.preventDefault();
searchInput.value = "";
performSearch();
}
});
})();

43
theme/style.css Normal file
View File

@@ -0,0 +1,43 @@
@font-face {
font-family: "Monaspace Argon";
src:
url("assets/MonaspaceArgonVar.woff2") format("woff2"),
url("assets/MonaspaceArgonVar.woff") format("woff");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
:root {
color-scheme: light dark;
}
html {
font-family:
"Monaspace Argon",
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Helvetica,
Arial,
sans-serif;
}
body {
font-family:
"Monaspace Argon",
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Helvetica,
Arial,
sans-serif;
}
code,
pre {
font-family: "Monaspace Argon", monospace;
font-feature-settings:
"calt", "ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10",
"liga";
}