Improve docs search UX, dash cleanup, and colored modality tool icons (#338)

* feat(docs): improve search UX

- Hide the 'Search by Pagefind' branding in the search dialog footer
- Replace the 'No results found.' message shown before any query with a
  friendly hint (detected via empty-input :placeholder-shown state)
- Tune placeholder, empty-state, and results-heading copy; show a few
  sub-section matches per result (pageResultCount)

* docs: replace double-dash em-dash substitute with single dash

Swept prose ' -- ' to ' - ' and numeric ranges (e.g. 2--20 to 2-20)
across the documentation. CLI flags and code blocks left untouched.

* fix(landing): show colored category icons on modality tool pages

The /tools/<modality>/ pages rendered bare name+description cards with no
icon or color. Port the colored, category-tinted icon card from /tools/
so modality pages match the main catalog (icon, tint, 'Learn more').
This commit is contained in:
SnapOtter
2026-06-24 12:06:31 +08:00
committed by GitHub
parent deace744c9
commit 607c24895b
20 changed files with 182 additions and 73 deletions
@@ -1,5 +1,6 @@
---
import { SECTIONS, TOOLS, toolSection } from "@snapotter/shared";
import { CATEGORIES, SECTIONS, TOOLS, toolSection } from "@snapotter/shared";
import * as lucideIcons from "lucide";
import Footer from "@/components/Footer.astro";
import Navbar from "@/components/Navbar.astro";
import Base from "@/layouts/Base.astro";
@@ -11,7 +12,22 @@ export function getStaticPaths() {
}));
}
function renderIcon(iconName: string): string {
const iconData = (lucideIcons as Record<string, unknown>)[iconName];
if (!iconData || !Array.isArray(iconData))
return '<rect width="14" height="14" x="5" y="5" rx="2" />';
return iconData
.map(([tag, attrs]: [string, Record<string, string>]) => {
const attrStr = Object.entries(attrs)
.map(([k, v]) => `${k}="${v}"`)
.join(" ");
return `<${tag} ${attrStr}/>`;
})
.join("");
}
const { section, tools } = Astro.props;
const categoryMap = new Map(CATEGORIES.map((c) => [c.id, c]));
const canonical = `https://snapotter.com/tools/${section.id}/`;
const title = `${section.name} Tools | SnapOtter`;
---
@@ -41,18 +57,45 @@ const title = `${section.name} Tools | SnapOtter`;
<section class="mx-auto max-w-6xl px-6 pt-8 pb-16">
<div class="reveal">
<h1 class="font-display text-3xl font-bold">{section.name} Tools</h1>
<ul class="mt-8 grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{tools.map((tool) => (
<li>
<a
class="card-hover block rounded-lg border border-border p-4"
href={`/tools/${section.id}/${tool.id}/`}
>
<span class="font-medium">{tool.name}</span>
<span class="mt-1 block text-sm text-muted">{tool.description}</span>
</a>
</li>
))}
<ul class="mt-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{tools.map((tool) => {
const cat = categoryMap.get(tool.category);
const color = cat?.color ?? "#E07832";
return (
<li>
<a
class="tool-item group flex items-start gap-4 rounded-xl border border-border bg-surface p-5 transition-all hover:border-primary hover:shadow-md"
href={`/tools/${section.id}/${tool.id}/`}
>
<div
class="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg"
style={`background-color: ${color}14`}
>
<svg
class="h-[18px] w-[18px]"
viewBox="0 0 24 24"
fill="none"
stroke={color}
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
set:html={renderIcon(tool.icon)}
/>
</div>
<div class="min-w-0">
<span class="text-sm font-bold">{tool.name}</span>
<p class="mt-1 line-clamp-2 text-xs leading-relaxed text-muted">{tool.description}</p>
<span class="mt-2 inline-flex items-center gap-1 text-xs font-medium text-primary transition-all group-hover:gap-2">
Learn more
<svg class="h-3 w-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M5 12h14" /><path d="m12 5 7 7-7 7" />
</svg>
</span>
</div>
</a>
</li>
);
})}
</ul>
</div>
</section>