Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion src/components/docs/DocsSidebar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { loadDocsMeta, sectionIconSvg, sectionLabel } from "./nav";
import { localePath, type Locale } from "../../i18n/config";
import { guides } from "../../i18n/guides/content";
import { explainers } from "../../i18n/guides/explainers";

/**
* Aiven-style docs sidebar: grouped sections with line icons, a Quickstart
Expand All @@ -14,9 +15,11 @@ interface Props {
current?: string;
/** Slug of the current Get Started guide, if rendering a guide page. */
guideSlug?: string;
/** Slug of the current explainer article, if rendering an article page. */
articleSlug?: string;
}

const { locale, current, guideSlug } = Astro.props;
const { locale, current, guideSlug, articleSlug } = Astro.props;
const meta = loadDocsMeta();

const quickstartLabel =
Expand All @@ -26,6 +29,8 @@ const quickstartLabel =
? "Essayez keel en cinq minutes"
: "Try keel in five minutes";
const guidesLabel = locale === "ar" ? "دليل البداية" : locale === "fr" ? "Premiers pas" : "Get started guides";
const explainersLabel =
locale === "ar" ? "شروحات معمّقة" : locale === "fr" ? "Approfondissements" : "Deep explainers";
---

<nav class="docs-sidebar" aria-label={locale === "ar" ? "تنقّل الوثائق" : "Docs navigation"}>
Expand Down Expand Up @@ -56,6 +61,32 @@ const guidesLabel = locale === "ar" ? "دليل البداية" : locale === "fr
</ul>
</div>

<div class="docs-side-section">
<p class="docs-side-heading">
<span class="docs-side-icon" set:html={sectionIconSvg("guides")} />
{explainersLabel}
</p>
<ul>
{
explainers.map((explainer) => {
const inLocale = locale === "ar" ? explainer.ar : explainer.en;
const href = locale === "ar" ? `/ar/guides/${explainer.slug}/` : `/en/guides/${explainer.slug}/`;
return (
<li>
<a
class:list={["docs-side-link", { current: articleSlug === explainer.slug }]}
href={href}
aria-current={articleSlug === explainer.slug ? "page" : undefined}
>
{inLocale.title}
</a>
</li>
);
})
}
</ul>
</div>

{
(["guides", "reference", "research"] as const).map((sectionId) => {
const section = meta.sections.find((s) => s.id === sectionId);
Expand Down
146 changes: 146 additions & 0 deletions src/components/pages/ArticlePage.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
import Base from "../../layouts/Base.astro";
import DocsSidebar from "../docs/DocsSidebar.astro";
import HonestBox from "../HonestBox.astro";
import { explainers } from "../../i18n/guides/explainers";
import { laneHref, laneNeighbors, laneTitle } from "../../i18n/guides/lane";
import { localePath, SITE, type Locale } from "../../i18n/config";

/**
* A deep explainer (#12): a long-form prose article in the guides lane —
* definition-first lede, honest-context box, prev/next across the whole
* lane. EN is the source; AR ships beside it with an FR-8 rev marker.
* Static markup only — no client JS.
*/
interface Props {
locale: Locale;
slug: string;
}

const { locale, slug } = Astro.props;
const explainer = explainers.find((e) => e.slug === slug);
if (!explainer) throw new Error(`unknown explainer: ${slug}`);
const article = locale === "ar" ? explainer.ar : explainer.en;
const otherLocale: Locale = locale === "ar" ? "en" : "ar";
const { prev, next } = laneNeighbors(slug);
const path = `/${locale}/guides/${slug}/`;

const jsonLd = [
{
"@context": "https://schema.org",
"@type": "Article",
headline: article.title,
description: article.description,
url: new URL(path, SITE).href,
inLanguage: locale,
isPartOf: { "@type": "WebSite", name: "keeltrading.com", url: SITE },
},
];

const ui =
locale === "ar"
? {
breadcrumbRoot: "الوثائق",
breadcrumbLane: "الشروحات",
prev: "السابق →",
next: "← التالي",
pagerLabel: "تنقّل الشروحات",
otherLocaleLabel: "Read this article in English",
}
: {
breadcrumbRoot: "Docs",
breadcrumbLane: "Guides",
prev: "← Previous",
next: "Next →",
pagerLabel: "Guide navigation",
otherLocaleLabel: "اقرأ هذه المقالة بالعربية",
};
---

<Base
locale={locale}
title={`${article.title} — keel guides`}
description={article.description}
path={path}
alternates={[
{ locale: "en", path: `/en/guides/${slug}/` },
{ locale: "ar", path: `/ar/guides/${slug}/` },
]}
jsonLd={jsonLd}
>
<div class="container">
<div class="docs-layout">
<DocsSidebar locale={locale} articleSlug={slug} />

<article class="docs-content" id="top">
<nav class="docs-breadcrumb" aria-label={locale === "ar" ? "مسار التنقّل" : "Breadcrumb"}>
<a href={localePath(locale, "docs")}>{ui.breadcrumbRoot}</a>
<span aria-hidden="true">›</span>
{locale === "en" ? <a href="/en/guides/">{ui.breadcrumbLane}</a> : <span>{ui.breadcrumbLane}</span>}
<span aria-hidden="true">›</span>
<span aria-current="page">{article.title}</span>
</nav>

<h1>{article.title}</h1>
<p class="lede">{article.lede}</p>

<div class="prose">
{
article.sections.map((section) => (
<section>
<h2>{section.heading}</h2>
{section.paragraphs.map((paragraph) => (
<p>{paragraph}</p>
))}
{section.bullets && (
<ul>
{section.bullets.map((bullet) => (
<li>{bullet}</li>
))}
</ul>
)}
</section>
))
}
</div>

<HonestBox title={article.honestBox.title} paragraphs={article.honestBox.paragraphs} links={article.honestBox.links} />

<p class="doc-meta">
{article.footnote}
<br />
<a href={`/${otherLocale}/guides/${slug}/`} lang={otherLocale}>{ui.otherLocaleLabel} →</a>
</p>

{
locale === "ar" && (
<p class="rev-marker">{`تُرجمت هذه المقالة عن المراجعة الإنجليزية ${explainer.ar.translatedFromRev}.`}</p>
)
}

<nav class="docs-pager" aria-label={ui.pagerLabel}>
{
prev ? (
<a class="docs-pager-link prev" href={laneHref(prev, locale)}>
<span class="docs-pager-dir">{ui.prev}</span>
<span class="docs-pager-title">{laneTitle(prev, locale)}</span>
</a>
) : (
<span />
)
}
{
next ? (
<a class="docs-pager-link next" href={laneHref(next, locale)}>
<span class="docs-pager-dir">{ui.next}</span>
<span class="docs-pager-title">{laneTitle(next, locale)}</span>
</a>
) : (
<span />
)
}
</nav>
</article>
</div>
</div>
</Base>
23 changes: 13 additions & 10 deletions src/components/pages/GuidePage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,26 @@
import Base from "../../layouts/Base.astro";
import DocsSidebar from "../docs/DocsSidebar.astro";
import { guides } from "../../i18n/guides/content";
import { laneHref, laneNeighbors, laneTitle } from "../../i18n/guides/lane";
import { SITE } from "../../i18n/config";

/**
* A Get Started guide page: numbered step sections, optional command blocks,
* and real TUI screenshots (public/get-started/). Editorial English — like
* the engine documents — surfaced through the docs sidebar.
* the engine documents — surfaced through the docs sidebar. Prev/next walks
* the whole guides lane, so the last Get Started guide continues into the
* deep explainers.
*/
interface Props {
slug: string;
}

const { slug } = Astro.props;
const index = guides.findIndex((guide) => guide.slug === slug);
if (index === -1) throw new Error(`unknown guide: ${slug}`);
const guide = guides[index]!;
const prev = index > 0 ? guides[index - 1]! : null;
const next = index < guides.length - 1 ? guides[index + 1]! : null;
const guide = guides.find((g) => g.slug === slug);
if (!guide) throw new Error(`unknown guide: ${slug}`);
const lane = laneNeighbors(slug);
const prev = lane.prev;
const next = lane.next;
const path = `/en/guides/${guide.slug}/`;

const jsonLd = [
Expand Down Expand Up @@ -78,19 +81,19 @@ const jsonLd = [
<nav class="docs-pager" aria-label="Guide navigation">
{
prev ? (
<a class="docs-pager-link prev" href={`/en/guides/${prev.slug}/`}>
<a class="docs-pager-link prev" href={laneHref(prev, "en")}>
<span class="docs-pager-dir">← Previous</span>
<span class="docs-pager-title">{prev.title}</span>
<span class="docs-pager-title">{laneTitle(prev, "en")}</span>
</a>
) : (
<span />
)
}
{
next ? (
<a class="docs-pager-link next" href={`/en/guides/${next.slug}/`}>
<a class="docs-pager-link next" href={laneHref(next, "en")}>
<span class="docs-pager-dir">Next →</span>
<span class="docs-pager-title">{next.title}</span>
<span class="docs-pager-title">{laneTitle(next, "en")}</span>
</a>
) : (
<span />
Expand Down
Loading
Loading