feat(site): add 17 real-world workflows showcase page and top-level nav entry
This commit is contained in:
1 parent
fae1c227d0
commit
e2279127b6
15 files changed
+1446
-4
No files matched your search
@@ -45,5 +45,10 @@ export default defineConfig({
|
||||
},
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
server: {
|
||||
fs: {
|
||||
allow: ["../.."],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,328 @@
|
||||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import Container from "@/components/container.astro";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { bestPracticesPageCopy, showcaseCaseAssets } from "@/data/best-practices";
|
||||
import { getLocalizedPath, getSiteLocale } from "@/i18n";
|
||||
|
||||
const locale = getSiteLocale(Astro.url.pathname);
|
||||
const copy = bestPracticesPageCopy[locale];
|
||||
const pageUrl = new URL(Astro.url.pathname, Astro.site).toString();
|
||||
|
||||
const allCases = copy.categories.flatMap((category) => category.cases);
|
||||
|
||||
const jsonLd = [
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "CollectionPage",
|
||||
name: copy.title,
|
||||
description: copy.description,
|
||||
url: pageUrl,
|
||||
inLanguage: locale,
|
||||
isPartOf: {
|
||||
"@type": "WebSite",
|
||||
name: "EdgeEver",
|
||||
url: new URL(getLocalizedPath(locale, "/"), Astro.site).toString(),
|
||||
},
|
||||
},
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "ItemList",
|
||||
name: copy.title,
|
||||
numberOfItems: allCases.length,
|
||||
itemListElement: allCases.map((item, idx) => ({
|
||||
"@type": "ListItem",
|
||||
position: idx + 1,
|
||||
name: `${item.number}. ${item.title}`,
|
||||
description: item.lead,
|
||||
url: `${pageUrl}#case-${item.number}`,
|
||||
})),
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
<Layout title={copy.title} description={copy.description} jsonLd={jsonLd}>
|
||||
<Container>
|
||||
<main class="mx-auto max-w-6xl py-10 md:py-16">
|
||||
<!-- Hero Header -->
|
||||
<header class="max-w-3xl">
|
||||
<span class="inline-flex items-center gap-1.5 px-3 py-1 text-xs font-bold tracking-widest text-emerald-700 bg-emerald-50 rounded-full border border-emerald-200/70 uppercase">
|
||||
<Icon name="bx:bx-grid-alt" class="w-3.5 h-3.5 text-emerald-600" />
|
||||
<span>{copy.eyebrow}</span>
|
||||
</span>
|
||||
<h1 class="mt-3.5 text-3xl sm:text-4xl md:text-5xl font-extrabold tracking-tight text-slate-900 leading-[1.15]">
|
||||
{copy.title}
|
||||
</h1>
|
||||
<p class="mt-4 text-sm sm:text-base md:text-lg leading-relaxed text-slate-600">
|
||||
{copy.description}
|
||||
</p>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="mt-6 flex flex-wrap items-center gap-3">
|
||||
<a
|
||||
href="https://demo.edgeever.org"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="inline-flex items-center gap-2 px-5 py-2.5 rounded-full text-xs sm:text-sm font-semibold text-white bg-brand hover:bg-brand-hover shadow-sm transition-all hover:-translate-y-0.5"
|
||||
>
|
||||
<Icon name="bx:bx-show" class="w-4 h-4" />
|
||||
<span>{copy.demoCta}</span>
|
||||
</a>
|
||||
<a
|
||||
href={getLocalizedPath(locale, "/blog/ai-agent-deploy-cloudflare")}
|
||||
class="inline-flex items-center gap-2 px-4 py-2.5 rounded-full text-xs sm:text-sm font-semibold text-brand-text bg-brand/5 border border-brand/20 hover:border-brand/40 hover:bg-brand/10 transition-all"
|
||||
>
|
||||
<Icon name="simple-icons:cloudflare" class="w-3.5 h-3.5" />
|
||||
<span>{copy.deployCta}</span>
|
||||
</a>
|
||||
<a
|
||||
href={copy.githubDocHref}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="inline-flex items-center gap-1.5 px-4 py-2.5 rounded-full text-xs sm:text-sm font-semibold text-slate-700 bg-white border border-slate-200/90 hover:border-slate-300 hover:bg-slate-50 transition-all"
|
||||
>
|
||||
<Icon name="bx:bxl-github" class="w-4 h-4 text-slate-800" />
|
||||
<span>{copy.githubDocCta}</span>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Sticky Category Bar & Scenario Directory -->
|
||||
<nav
|
||||
aria-label={copy.eyebrow}
|
||||
class="sticky top-3 z-30 mt-8 rounded-2xl border border-slate-200/90 bg-white/90 backdrop-blur-md p-2.5 sm:p-3 shadow-sm"
|
||||
>
|
||||
<div class="flex items-center justify-between gap-2 overflow-x-auto no-scrollbar">
|
||||
<div class="flex items-center gap-1.5 sm:gap-2 shrink-0">
|
||||
{
|
||||
copy.categories.map((cat) => (
|
||||
<a
|
||||
href={`#cat-${cat.id}`}
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs font-semibold text-slate-700 hover:text-emerald-700 hover:bg-emerald-50/80 border border-transparent hover:border-emerald-200/60 transition-colors whitespace-nowrap"
|
||||
>
|
||||
<span>{cat.badge}</span>
|
||||
<span class="rounded-full bg-slate-100 px-1.5 py-0.5 text-[10px] font-mono text-slate-500">
|
||||
{cat.cases.length}
|
||||
</span>
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<span class="hidden lg:inline-flex items-center gap-1.5 text-[11px] text-slate-400 pr-2 shrink-0">
|
||||
<Icon name="bx:bx-zoom-in" class="w-3.5 h-3.5 text-emerald-600" />
|
||||
<span>{copy.zoomHint}</span>
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Compact 17-Scenario Quick Index -->
|
||||
<div class="mt-4 rounded-2xl border border-slate-200/70 bg-slate-50/70 p-4 sm:p-5">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-x-4 gap-y-2">
|
||||
{
|
||||
allCases.map((item) => (
|
||||
<a
|
||||
href={`#case-${item.number}`}
|
||||
class="group flex items-center gap-2 py-1 text-xs sm:text-[13px] text-slate-600 hover:text-emerald-700 transition-colors truncate"
|
||||
>
|
||||
<span class="inline-flex h-5 w-6 shrink-0 items-center justify-center rounded bg-white border border-slate-200/80 font-mono text-[10.5px] font-bold text-emerald-700 group-hover:border-emerald-300 group-hover:bg-emerald-50 transition-colors">
|
||||
{item.number}
|
||||
</span>
|
||||
<span class="truncate font-medium">{item.title}</span>
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Categories & Cases -->
|
||||
<div class="mt-10 sm:mt-14 space-y-14 sm:space-y-20">
|
||||
{
|
||||
copy.categories.map((category, catIdx) => (
|
||||
<section id={`cat-${category.id}`} class="scroll-mt-24">
|
||||
<div class="border-b border-slate-200/80 pb-4 mb-6 sm:mb-8">
|
||||
<span class="inline-block px-2.5 py-0.5 text-[11px] font-bold text-emerald-700 bg-emerald-100/70 rounded-md">
|
||||
{category.badge}
|
||||
</span>
|
||||
<h2 class="mt-2 text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight">
|
||||
{category.title}
|
||||
</h2>
|
||||
<p class="mt-1.5 text-xs sm:text-sm md:text-base text-slate-600">
|
||||
{category.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-8 sm:space-y-10">
|
||||
{category.cases.map((caseItem, caseIdx) => {
|
||||
const assetConfig = showcaseCaseAssets[caseItem.id];
|
||||
const isFirstCase = catIdx === 0 && caseIdx === 0;
|
||||
const gridClass =
|
||||
assetConfig.layout === "grid-3"
|
||||
? "grid grid-cols-1 sm:grid-cols-3 gap-4 sm:gap-5 items-start"
|
||||
: assetConfig.layout === "grid-2"
|
||||
? "grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 items-start"
|
||||
: "flex flex-col gap-5 sm:gap-6";
|
||||
|
||||
return (
|
||||
<article
|
||||
id={`case-${caseItem.number}`}
|
||||
class="scroll-mt-24 rounded-2xl sm:rounded-3xl border border-slate-200/90 bg-white p-5 sm:p-7 md:p-8 shadow-xs transition-shadow hover:shadow-md"
|
||||
>
|
||||
<!-- Case Header -->
|
||||
<div class="max-w-4xl">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<span class="inline-flex items-center justify-center px-2.5 py-0.5 rounded-lg bg-emerald-600 text-white font-mono text-xs font-bold">
|
||||
#{caseItem.number}
|
||||
</span>
|
||||
<h3 class="text-lg sm:text-xl md:text-2xl font-bold text-slate-900 tracking-tight">
|
||||
{caseItem.title}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<p class="mt-3 text-xs sm:text-sm md:text-base leading-relaxed text-slate-600">
|
||||
{caseItem.lead}
|
||||
</p>
|
||||
|
||||
<ul class="mt-3 space-y-1.5 text-xs sm:text-sm text-slate-600">
|
||||
{caseItem.bullets.map((bullet) => (
|
||||
<li class="flex items-start gap-2 leading-relaxed">
|
||||
<span class="mt-1.5 h-1.5 w-1.5 rounded-full bg-emerald-500 shrink-0" />
|
||||
<span>{bullet}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{caseItem.links && caseItem.links.length > 0 && (
|
||||
<div class="mt-3.5 flex flex-wrap items-center gap-2">
|
||||
{caseItem.links.map((link) => (
|
||||
<a
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="inline-flex items-center gap-1 px-3 py-1 rounded-lg text-xs font-semibold text-emerald-700 bg-emerald-50 hover:bg-emerald-100/80 border border-emerald-200/70 transition-colors"
|
||||
>
|
||||
<span>{link.label}</span>
|
||||
<Icon name="bx:bx-link-external" class="w-3.5 h-3.5" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<!-- Case Screenshots -->
|
||||
<div class={`mt-5 sm:mt-6 ${gridClass}`}>
|
||||
{caseItem.media.map((mediaItem, mediaIdx) => {
|
||||
const imgAsset = assetConfig.images[mediaIdx];
|
||||
if (!imgAsset) return null;
|
||||
|
||||
return (
|
||||
<figure class="flex flex-col rounded-xl sm:rounded-2xl border border-slate-200/80 bg-white overflow-hidden shadow-2xs">
|
||||
<figcaption class="flex items-center justify-between gap-2 px-4 py-2.5 border-b border-slate-200/70 bg-slate-50/80 text-xs sm:text-sm font-semibold text-slate-700">
|
||||
<span class="truncate">{mediaItem.caption}</span>
|
||||
<span class="text-[11px] font-normal text-slate-400 shrink-0 flex items-center gap-1">
|
||||
<Icon name="bx:bx-fullscreen" class="w-3.5 h-3.5" />
|
||||
</span>
|
||||
</figcaption>
|
||||
<button
|
||||
type="button"
|
||||
data-lightbox-src={imgAsset.src}
|
||||
data-lightbox-alt={mediaItem.alt}
|
||||
data-lightbox-caption={`${caseItem.number}. ${caseItem.title} — ${mediaItem.caption}`}
|
||||
class="relative block w-full overflow-hidden bg-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/50 cursor-zoom-in"
|
||||
aria-label={`${mediaItem.caption} - ${copy.zoomHint}`}
|
||||
>
|
||||
<img
|
||||
src={imgAsset.src}
|
||||
width={imgAsset.width}
|
||||
height={imgAsset.height}
|
||||
alt={mediaItem.alt}
|
||||
loading={isFirstCase && mediaIdx === 0 ? "eager" : "lazy"}
|
||||
decoding="async"
|
||||
class="w-full h-auto block mx-auto"
|
||||
/>
|
||||
</button>
|
||||
</figure>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</main>
|
||||
</Container>
|
||||
|
||||
<!-- Image Lightbox Dialog -->
|
||||
<dialog
|
||||
id="showcase-lightbox"
|
||||
class="m-auto max-h-[92vh] max-w-[94vw] rounded-2xl border border-slate-700/80 bg-slate-950/95 p-2 sm:p-4 text-white shadow-2xl backdrop:bg-slate-950/75 backdrop:backdrop-blur-xs"
|
||||
>
|
||||
<div class="flex flex-col gap-2.5 max-h-[88vh]">
|
||||
<div class="flex items-center justify-between gap-4 px-2 pt-1">
|
||||
<p id="showcase-lightbox-caption" class="text-xs sm:text-sm font-medium text-slate-200 truncate"></p>
|
||||
<button
|
||||
type="button"
|
||||
id="showcase-lightbox-close"
|
||||
aria-label={copy.closePreview}
|
||||
class="inline-flex items-center gap-1 rounded-lg bg-slate-800 hover:bg-slate-700 px-2.5 py-1 text-xs font-semibold text-slate-200 transition-colors shrink-0 cursor-pointer"
|
||||
>
|
||||
<span>ESC</span>
|
||||
<Icon name="bx:bx-x" class="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="overflow-auto flex items-center justify-center rounded-xl bg-slate-900/60 p-1">
|
||||
<img
|
||||
id="showcase-lightbox-img"
|
||||
src=""
|
||||
alt=""
|
||||
class="max-h-[80vh] max-w-[90vw] w-auto h-auto object-contain rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
</Layout>
|
||||
|
||||
<script is:inline>
|
||||
(function () {
|
||||
function initLightbox() {
|
||||
var dialog = document.getElementById("showcase-lightbox");
|
||||
var img = document.getElementById("showcase-lightbox-img");
|
||||
var caption = document.getElementById("showcase-lightbox-caption");
|
||||
var closeBtn = document.getElementById("showcase-lightbox-close");
|
||||
if (!dialog || !img || !caption) return;
|
||||
|
||||
document.querySelectorAll("[data-lightbox-src]").forEach(function (btn) {
|
||||
btn.addEventListener("click", function () {
|
||||
var src = btn.getAttribute("data-lightbox-src");
|
||||
var alt = btn.getAttribute("data-lightbox-alt") || "";
|
||||
var cap = btn.getAttribute("data-lightbox-caption") || alt;
|
||||
if (!src) return;
|
||||
img.setAttribute("src", src);
|
||||
img.setAttribute("alt", alt);
|
||||
caption.textContent = cap;
|
||||
if (typeof dialog.showModal === "function") {
|
||||
dialog.showModal();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (closeBtn) {
|
||||
closeBtn.addEventListener("click", function () {
|
||||
dialog.close();
|
||||
});
|
||||
}
|
||||
|
||||
dialog.addEventListener("click", function (e) {
|
||||
if (e.target === dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initLightbox();
|
||||
document.addEventListener("astro:page-load", initLightbox);
|
||||
})();
|
||||
</script>
|
||||
@@ -1,17 +1,17 @@
|
||||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { getSiteLocale, siteCopy } from "@/i18n";
|
||||
import { getLocalizedPath, getSiteLocale, siteCopy } from "@/i18n";
|
||||
|
||||
const locale = getSiteLocale(Astro.url.pathname);
|
||||
const bento = siteCopy[locale].bento;
|
||||
---
|
||||
|
||||
<section class="mt-12 md:mt-24 relative overflow-hidden" aria-labelledby="bento-features">
|
||||
<section id="features" class="mt-12 md:mt-24 relative overflow-hidden scroll-mt-6" aria-labelledby="bento-features">
|
||||
<!-- Subtle Glow background -->
|
||||
<div class="absolute top-1/3 left-1/2 -translate-x-1/2 w-[700px] h-[400px] bg-emerald-500/5 rounded-full blur-3xl -z-10 pointer-events-none"></div>
|
||||
|
||||
<!-- Header -->
|
||||
<div class="max-w-3xl mb-8 md:mb-16 text-center mx-auto px-4">
|
||||
<div class="max-w-3xl mb-8 md:mb-14 text-center mx-auto px-4">
|
||||
<span class="inline-block px-3.5 py-1 text-xs font-bold tracking-widest text-emerald-600 bg-emerald-50 rounded-full border border-emerald-200/60 uppercase">
|
||||
{bento.eyebrow}
|
||||
</span>
|
||||
@@ -21,6 +21,18 @@ const bento = siteCopy[locale].bento;
|
||||
<p class="text-slate-600 mt-3 sm:mt-4 text-xs sm:text-sm md:text-base leading-relaxed max-w-2xl mx-auto">
|
||||
{bento.subheading}
|
||||
</p>
|
||||
<div class="mt-5 flex justify-center">
|
||||
<a
|
||||
href={getLocalizedPath(locale, "/guides/best-practices")}
|
||||
class="group inline-flex items-center gap-2 px-4 py-2 rounded-full text-xs sm:text-sm font-semibold text-emerald-800 bg-emerald-50/90 hover:bg-emerald-100/90 border border-emerald-200/80 hover:border-emerald-300 shadow-2xs transition-all hover:-translate-y-0.5"
|
||||
>
|
||||
<Icon name="bx:bx-images" class="w-4 h-4 text-emerald-600 shrink-0" />
|
||||
<span>{bento.showcaseCta}</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-3.5 h-3.5 text-emerald-600 transition-transform group-hover:translate-x-0.5 shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bento Grid Container -->
|
||||
|
||||
@@ -30,6 +30,10 @@ const copy = siteCopy[locale].nav;
|
||||
{locale === "zh-CN" ? "特别致谢" : locale === "ja" ? "謝辞" : locale === "pl" ? "Podziękowania" : "Thanks"}
|
||||
</a>
|
||||
<span class="text-slate-300">•</span>
|
||||
<a href={getLocalizedPath(locale, "/guides/best-practices")} class="inline-flex min-h-11 sm:min-h-0 items-center hover:text-emerald-700 transition-colors">
|
||||
{copy.bestPractices}
|
||||
</a>
|
||||
<span class="text-slate-300">•</span>
|
||||
<a href={getLocalizedPath(locale, "/self-hosted-evernote-alternative")} class="inline-flex min-h-11 sm:min-h-0 items-center hover:text-emerald-700 transition-colors">
|
||||
{copy.selfHostedAlternative}
|
||||
</a>
|
||||
|
||||
@@ -18,7 +18,7 @@ const copy = siteCopy[locale].guides;
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 md:mt-8 grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||
<div class="mt-6 md:mt-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{
|
||||
copy.items.map((item) => (
|
||||
<a
|
||||
|
||||
@@ -12,6 +12,10 @@ const menuitems = [
|
||||
title: copy.features,
|
||||
path: `${getLocalizedPath(locale, "/")}#features`,
|
||||
},
|
||||
{
|
||||
title: copy.bestPractices,
|
||||
path: getLocalizedPath(locale, "/guides/best-practices"),
|
||||
},
|
||||
{
|
||||
title: copy.deploy,
|
||||
children: [
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -22,6 +22,7 @@ export const jaSiteCopy = {
|
||||
memosMigration: "Memos から移行",
|
||||
notionMigration: "Notion から移行",
|
||||
flomoMigration: "Flomo から移行",
|
||||
bestPractices: "実機デモ",
|
||||
advancedPlay: "AI Agent の使い方",
|
||||
blog: "ブログ",
|
||||
backToBlog: "ブログ一覧へ",
|
||||
@@ -54,6 +55,7 @@ export const jaSiteCopy = {
|
||||
heading: "セルフホストの知識管理と AI のために作り直した",
|
||||
subheading:
|
||||
"広告、端末数の制限、高いサブスクはやめましょう。速さ、開放性、自分で守れるプライバシーを前提にしています。",
|
||||
showcaseCta: "17 の実機デモとベストプラクティスを見る",
|
||||
card1: {
|
||||
badge: "クラシック UI",
|
||||
title: "三ペインはそのまま、より軽く速く",
|
||||
@@ -270,6 +272,12 @@ export const jaSiteCopy = {
|
||||
href: "/blog/evernote-migration-guide",
|
||||
cta: "移行ガイドを読む",
|
||||
},
|
||||
{
|
||||
title: "17 の実機デモとベストプラクティス",
|
||||
summary: "三ペイン執筆、マルチチャネル取り込み、AI 図解・多次元テーブル、ローカル ACP 連携、ポスター作成の実機例。",
|
||||
href: "/guides/best-practices",
|
||||
cta: "実機デモを見る",
|
||||
},
|
||||
{
|
||||
title: "AI Agent の応用",
|
||||
summary: "MCP で、実ノートから知識マップ、タグ整理、一段上の個人知識ワークフローを作れます。",
|
||||
|
||||
@@ -22,6 +22,7 @@ export const plSiteCopy = {
|
||||
memosMigration: "Migracja z Memos",
|
||||
notionMigration: "Migracja z Notion",
|
||||
flomoMigration: "Migracja z Flomo",
|
||||
bestPractices: "Prezentacja",
|
||||
advancedPlay: "Praca z agentami AI",
|
||||
blog: "Blog",
|
||||
backToBlog: "Wróć do bloga",
|
||||
@@ -54,6 +55,7 @@ export const plSiteCopy = {
|
||||
heading: "Zbudowany od nowa z myślą o samodzielnym hostingu wiedzy i pracy z AI",
|
||||
subheading:
|
||||
"Bez reklam, limitów urządzeń i drogich subskrypcji. Każdy szczegół służy szybkiemu, otwartemu i prywatnemu „drugiemu mózgowi”.",
|
||||
showcaseCta: "Zobacz 17 praktycznych scenariuszy i zrzutów ekranu",
|
||||
card1: {
|
||||
badge: "Klasyczny interfejs",
|
||||
title: "Klasyczny układ trzech paneli – szybszy i lżejszy",
|
||||
@@ -270,6 +272,12 @@ export const plSiteCopy = {
|
||||
href: "/blog/evernote-migration-guide",
|
||||
cta: "Przeczytaj poradnik migracji",
|
||||
},
|
||||
{
|
||||
title: "17 praktycznych scenariuszy",
|
||||
summary: "Wizualny przegląd pisania w trzech panelach, wycinania treści, diagramów i tabel AI, lokalnych agentów ACP i plakatów.",
|
||||
href: "/guides/best-practices",
|
||||
cta: "Zobacz przykłady",
|
||||
},
|
||||
{
|
||||
title: "Zaawansowana praca z agentami AI",
|
||||
summary: "Zamieniaj prawdziwe notatki w mapy wiedzy, plany porządkowania tagów i bardziej zaawansowane osobiste przepływy wiedzy dzięki MCP.",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { bestPracticesPageCopy, showcaseCaseAssets } from "./data/best-practices.ts";
|
||||
import { alternativePageCopy } from "./data/self-hosted-evernote-alternative.ts";
|
||||
import {
|
||||
getLocalizedPath,
|
||||
@@ -88,4 +89,19 @@ describe("site locale routing", () => {
|
||||
expect(siteCopy.pl.layout.ogLocale).toBe("pl_PL");
|
||||
expect(siteCopy.pl.nav.backToBlog).toBe("Wróć do bloga");
|
||||
});
|
||||
|
||||
test("keeps best practices showcase copy and media counts aligned across locales", () => {
|
||||
for (const locale of siteLocales) {
|
||||
expect(collectKeys(bestPracticesPageCopy[locale])).toEqual(
|
||||
collectKeys(bestPracticesPageCopy["zh-CN"]),
|
||||
);
|
||||
const cases = bestPracticesPageCopy[locale].categories.flatMap((c) => c.cases);
|
||||
expect(cases).toHaveLength(17);
|
||||
for (const item of cases) {
|
||||
const assets = showcaseCaseAssets[item.id];
|
||||
expect(assets).toBeDefined();
|
||||
expect(item.media).toHaveLength(assets.images.length);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -114,6 +114,7 @@ export const siteCopy = {
|
||||
memosMigration: "从 Memos 迁移",
|
||||
notionMigration: "从 Notion 迁移",
|
||||
flomoMigration: "从 Flomo 迁移",
|
||||
bestPractices: "真机演示",
|
||||
advancedPlay: "搭配AI Agent的玩法",
|
||||
blog: "博客",
|
||||
backToBlog: "返回博客",
|
||||
@@ -144,6 +145,7 @@ export const siteCopy = {
|
||||
eyebrow: "WHY EDGEEVER",
|
||||
heading: "重新定义自托管知识工作区",
|
||||
subheading: "告别商业笔记的臃肿广告、设备限制与昂贵订阅。每一个设计细节,都旨在打造流畅、开放、隐私可控且原生支持 AI 的第二大脑。",
|
||||
showcaseCta: "查看 17 个真机案例演示与最佳实践",
|
||||
card1: {
|
||||
badge: "经典重现",
|
||||
title: "经典三栏与专注模式,熟悉更轻快",
|
||||
@@ -359,6 +361,12 @@ export const siteCopy = {
|
||||
href: "/blog/evernote-migration-guide",
|
||||
cta: "查看迁移指南",
|
||||
},
|
||||
{
|
||||
title: "17 个真机场景与实操指南",
|
||||
summary: "浏览三栏写作、微信与全渠道剪藏、AI 导图/信息图/多维表格、本机 ACP 协同及海报排版实机演示。",
|
||||
href: "/guides/best-practices",
|
||||
cta: "查看实机案例",
|
||||
},
|
||||
{
|
||||
title: "AI Agent 进阶玩法",
|
||||
summary: "用 MCP 读取真实笔记,生成知识地图、标签建议和个人资料整理工作流。",
|
||||
@@ -389,6 +397,7 @@ export const siteCopy = {
|
||||
memosMigration: "Migrate from Memos",
|
||||
notionMigration: "Migrate from Notion",
|
||||
flomoMigration: "Migrate from Flomo",
|
||||
bestPractices: "Showcase",
|
||||
advancedPlay: "AI Agent plays",
|
||||
blog: "Blog",
|
||||
backToBlog: "Back to blog",
|
||||
@@ -419,6 +428,7 @@ export const siteCopy = {
|
||||
eyebrow: "WHY EDGEEVER",
|
||||
heading: "Rebuilt for Self-Hosted Knowledge & AI Workflows",
|
||||
subheading: "Say goodbye to ads, device caps, and expensive subscriptions. Every detail is crafted for a fast, open, and private second brain.",
|
||||
showcaseCta: "Explore 17 real-world workflows & screenshots",
|
||||
card1: {
|
||||
badge: "Classic UI",
|
||||
title: "Classic Three-Pane Layout, Faster & Lighter",
|
||||
@@ -634,6 +644,12 @@ export const siteCopy = {
|
||||
href: "/blog/evernote-migration-guide",
|
||||
cta: "Read migration guide",
|
||||
},
|
||||
{
|
||||
title: "17 Real-World Workflows",
|
||||
summary: "Visual walkthrough of three-pane editing, omnichannel clipping, AI diagrams & databases, local ACP agents, and poster design.",
|
||||
href: "/guides/best-practices",
|
||||
cta: "View showcase",
|
||||
},
|
||||
{
|
||||
title: "AI Agent advanced play",
|
||||
summary: "Turn real notes into knowledge maps, tag cleanup plans, and higher-level personal knowledge workflows through MCP.",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
import BestPracticesShowcase from "@/components/best-practices-showcase.astro";
|
||||
---
|
||||
|
||||
<BestPracticesShowcase />
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
import BestPracticesShowcase from "@/components/best-practices-showcase.astro";
|
||||
---
|
||||
|
||||
<BestPracticesShowcase />
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
import BestPracticesShowcase from "@/components/best-practices-showcase.astro";
|
||||
---
|
||||
|
||||
<BestPracticesShowcase />
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
import BestPracticesShowcase from "@/components/best-practices-showcase.astro";
|
||||
---
|
||||
|
||||
<BestPracticesShowcase />
|
||||
Reference in new issue
Block a user