From bd8ba6e67ce3c86ebcc59c3c53d19b88b28d40d5 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Fri, 11 Sep 2026 23:49:59 +0530 Subject: [PATCH] feat: regroup homepage into apps/tutorial/services/libraries cards - assign each repo a section via config.yml (blog -> apps, media-manager -> tutorial, auth-server -> services, rest -> libraries) - render one card per repo with a bottom-justified action row containing one link per available kind (view docs / view wiki / view lib / view tutorial) - switch card grid to three columns with responsive breakpoints and taller cards --- _index/index.html | 140 ++++++++++++++++++++++++++++------------------ collect.py | 110 +++++++++++++++++++++++------------- config.yml | 14 ++++- 3 files changed, 170 insertions(+), 94 deletions(-) diff --git a/_index/index.html b/_index/index.html index 231d9a6..79c4a9a 100644 --- a/_index/index.html +++ b/_index/index.html @@ -30,19 +30,25 @@ padding:3px 8px; border-radius:6px; font-size:0.85rem; font-weight:600; border:1px solid #484f58; } - .section { width:100%; max-width:900px; margin-bottom:2.5rem; } + .section { width:100%; max-width:1200px; margin-bottom:2.5rem; } .section h2 { font-size:1.2rem; color:var(--muted); text-transform:uppercase; letter-spacing:1.5px; margin-bottom:1rem; border-bottom:1px solid var(--border); padding-bottom:0.4rem; } .grid { - display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); - gap:1.5rem; width:100%; + display:grid; grid-template-columns:repeat(3, 1fr); gap:1.5rem; width:100%; + } + @media (max-width: 1024px) { + .grid { grid-template-columns:repeat(2, 1fr); } + } + @media (max-width: 640px) { + .grid { grid-template-columns:1fr; } } .card { background:var(--card); border:1px solid var(--border); border-radius:12px; - padding:1.8rem; text-align:left; + padding:1.6rem; text-align:left; display:flex; flex-direction:column; + height:100%; min-height:200px; transition:transform 0.15s ease,box-shadow 0.15s ease; } .card:hover { @@ -51,11 +57,19 @@ } .card h3 { color:var(--accent); font-size:1.4rem; margin-bottom:0.4rem; } .card p { color:var(--muted); font-size:0.95rem; line-height:1.6; } - .card a { - color:var(--accent); text-decoration:none; display:inline-block; - margin-top:0.8rem; font-weight:500; + .card-actions { + margin-top:auto; padding-top:1.2rem; display:flex; flex-wrap:wrap; gap:0.6rem; + } + .card-actions a { + color:var(--accent); text-decoration:none; font-size:0.85rem; font-weight:600; + padding:0.45rem 0.9rem; border:1px solid var(--border); border-radius:999px; + background:rgba(45,212,191,0.06); + transition:border-color 0.15s ease,background 0.15s ease,color 0.15s ease; + } + .card-actions a:hover { + color:var(--accent-dim); border-color:var(--accent); + background:rgba(45,212,191,0.14); } - .card a:hover { color:var(--accent-dim); text-decoration:underline; } footer { margin-top:4rem; text-align:center; color:var(--muted); font-size:0.9rem; } @@ -65,86 +79,104 @@

Central documentation hub for Aetoskia projects

+
+

Apps

+
+
+

Blog API

+

A modular, async Blog API backend for FastAPI microservices. Provides streamlined CRUD operations for articles and authors.

+
+ view docs +
+
+
+
+ +
+

Tutorial

+
+
+

Media Manager Tutorial

+

Step-by-step guide to the Media Manager architecture, ingestion pipeline, automation workflows, and integrations.

+ +
+
+
+ +
+

Services

+
+
+

Auth Server

+

Aetoskia authentication service. OpenAPI-driven, FastAPI backend for auth flows.

+
+ view docs +
+
+
+
+

Libraries

OpenAPI First

Design-first API development toolkit enforcing OpenAPI contracts as the single source of truth.

- View Documentation → +
+ view lib +

Doc Forge

Renderer-agnostic Python documentation compiler powering MkDocs and MCP generation across Aetoskia projects.

- View Documentation → +

DAG Pipe

Deterministic pipeline framework for executing state transformations through a directed acyclic graph (DAG).

- View Documentation → +
+ view lib +

Mail Intake

High-performance email ingestion and processing framework with pluggable adapters and automation workflows.

- View Documentation → +
+ view lib +

Omniread

Unified ingestion and normalization layer for structured and unstructured data sources.

- View Documentation → +
+ view lib +

Py JWT

Lightweight, explicit JWT utilities for Python services with strong validation and minimal magic.

- View Documentation → +
+ view lib +

Hexa

Hexagonal architecture framework and toolbox for Python services.

- View Documentation → -
-
-
- -
-

APIs

-
-
-

Auth Server

-

Aetoskia authentication service. OpenAPI-driven, FastAPI backend for auth flows.

- View Documentation → -
-
-

Blog API

-

A modular, async Blog API backend for FastAPI microservices. Provides streamlined CRUD operations for articles and authors.

- View Documentation → -
-
-
- -
-

Wiki

-
-
-

Doc Forge

-

Renderer-agnostic Python documentation compiler powering MkDocs and MCP generation across Aetoskia projects.

- View Documentation → +
+ view lib +

Mongo Ops

A modular, async MongoDB operations layer for FastAPI microservices. Simplifies CRUD, transactions, and model management.

- View Documentation → -
-
-
- -
-

Tutorials

-
-
-

Media Manager Tutorial

-

Step-by-step guide to the Media Manager architecture, ingestion pipeline, automation workflows, and integrations.

- View Tutorial → +
+ view wiki +
diff --git a/collect.py b/collect.py index 27b79a3..44804bc 100644 --- a/collect.py +++ b/collect.py @@ -107,19 +107,25 @@ INDEX_TEMPLATE = r""" padding:3px 8px; border-radius:6px; font-size:0.85rem; font-weight:600; border:1px solid #484f58; }} - .section {{ width:100%; max-width:900px; margin-bottom:2.5rem; }} + .section {{ width:100%; max-width:1200px; margin-bottom:2.5rem; }} .section h2 {{ font-size:1.2rem; color:var(--muted); text-transform:uppercase; letter-spacing:1.5px; margin-bottom:1rem; border-bottom:1px solid var(--border); padding-bottom:0.4rem; }} .grid {{ - display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); - gap:1.5rem; width:100%; + display:grid; grid-template-columns:repeat(3, 1fr); gap:1.5rem; width:100%; + }} + @media (max-width: 1024px) {{ + .grid {{ grid-template-columns:repeat(2, 1fr); }} + }} + @media (max-width: 640px) {{ + .grid {{ grid-template-columns:1fr; }} }} .card {{ background:var(--card); border:1px solid var(--border); border-radius:12px; - padding:1.8rem; text-align:left; + padding:1.6rem; text-align:left; display:flex; flex-direction:column; + height:100%; min-height:200px; transition:transform 0.15s ease,box-shadow 0.15s ease; }} .card:hover {{ @@ -128,11 +134,19 @@ INDEX_TEMPLATE = r""" }} .card h3 {{ color:var(--accent); font-size:1.4rem; margin-bottom:0.4rem; }} .card p {{ color:var(--muted); font-size:0.95rem; line-height:1.6; }} - .card a {{ - color:var(--accent); text-decoration:none; display:inline-block; - margin-top:0.8rem; font-weight:500; + .card-actions {{ + margin-top:auto; padding-top:1.2rem; display:flex; flex-wrap:wrap; gap:0.6rem; + }} + .card-actions a {{ + color:var(--accent); text-decoration:none; font-size:0.85rem; font-weight:600; + padding:0.45rem 0.9rem; border:1px solid var(--border); border-radius:999px; + background:rgba(45,212,191,0.06); + transition:border-color 0.15s ease,background 0.15s ease,color 0.15s ease; + }} + .card-actions a:hover {{ + color:var(--accent-dim); border-color:var(--accent); + background:rgba(45,212,191,0.14); }} - .card a:hover {{ color:var(--accent-dim); text-decoration:underline; }} footer {{ margin-top:4rem; text-align:center; color:var(--muted); font-size:0.9rem; }} @@ -151,16 +165,45 @@ INDEX_TEMPLATE = r""" """ -def _render_section(title: str, cards: list[tuple[str, str, str, str]]) -> str: +SECTION_ORDER = ( + ("apps", "Apps"), + ("tutorial", "Tutorial"), + ("services", "Services"), + ("libraries", "Libraries"), +) +DEFAULT_SECTION = "libraries" + +LINK_KINDS = (("api", "view docs"), ("wiki", "view wiki"), ("lib", "view lib")) + + +def _card_links( + entry: dict, name: str, base_url: str, kind_urls: dict[tuple[str, str], str] +) -> list[tuple[str, str]]: + """Build the action links for a repo card from its declared kinds.""" + docs = entry.get("docs") or {} + links: list[tuple[str, str]] = [] + for kind, label in LINK_KINDS: + if docs.get(kind): + url = kind_urls.get((name, kind), base_url) + links.append((label, url)) + if entry.get("kind") == "tutorial": + links.append(("view tutorial", base_url)) + return links or [("view docs", base_url)] + + +def _render_section(title: str, cards: list[tuple[str, str, list[tuple[str, str]]]]) -> str: if not cards: return "" lines = [f'
', f"

{title}

", '
'] - for t, desc, href, label in cards: + for t, desc, links in cards: lines.append(f'
') lines.append(f"

{t}

") lines.append(f"

{desc}

") - lines.append(f' {label} →') - lines.append(f"
") + lines.append('
') + for label, href in links: + lines.append(f' {label}') + lines.append("
") + lines.append("
") lines.append("") lines.append("
") return "\n".join(lines) @@ -171,46 +214,35 @@ def build_index_html( home_urls: dict[str, str], kind_urls: dict[tuple[str, str], str] | None = None, ) -> str: - lib_cards: list[tuple[str, str, str, str]] = [] - api_cards: list[tuple[str, str, str, str]] = [] - wiki_cards: list[tuple[str, str, str, str]] = [] - tutorial_cards: list[tuple[str, str, str, str]] = [] - kind_urls = kind_urls or {} + sections: dict[str, list[tuple[str, str, list[tuple[str, str]]]]] = {} for entry in config.get("repos", []): - docs = entry.get("docs") or {} name = entry["name"] title = _safe_title(entry) desc = _safe_description(entry) base_url = home_urls.get(name, f"/{name}/") - if docs.get("lib"): - url = kind_urls.get((name, "lib"), base_url) - lib_cards.append((title, desc, url, "View Documentation")) - if docs.get("wiki"): - url = kind_urls.get((name, "wiki"), base_url) - wiki_cards.append((title, desc, url, "View Documentation")) - if docs.get("api"): - url = kind_urls.get((name, "api"), base_url) - api_cards.append((title, desc, url, "View Documentation")) + section = (entry.get("section") or DEFAULT_SECTION).strip().lower() + sections.setdefault(section, []).append( + (title, desc, _card_links(entry, name, base_url, kind_urls)) + ) for entry in config.get("static", []): - kind = entry.get("kind") name = entry["repo"] title = _safe_title(entry) desc = _safe_description(entry) - url = home_urls.get(name, f"/{name}/") - if kind == "api": - api_cards.append((title, desc, url, "View Documentation")) - elif kind == "tutorial": - tutorial_cards.append((title, desc, url, "View Tutorial")) + base_url = home_urls.get(name, f"/{name}/") + section = (entry.get("section") or DEFAULT_SECTION).strip().lower() + sections.setdefault(section, []).append( + (title, desc, _card_links(entry, name, base_url, kind_urls)) + ) - body = "\n\n".join(filter(None, [ - _render_section("Libraries", lib_cards), - _render_section("APIs", api_cards), - _render_section("Wiki", wiki_cards), - _render_section("Tutorials", tutorial_cards), - ])) + body = "\n\n".join( + filter( + None, + (_render_section(title, sections.get(key, [])) for key, title in SECTION_ORDER), + ) + ) return INDEX_TEMPLATE.format(body=body) diff --git a/config.yml b/config.yml index 3442e8b..6bb1d79 100644 --- a/config.yml +++ b/config.yml @@ -12,7 +12,8 @@ # wiki -> // when the wiki is the whole site (e.g. mongo-ops) # -> //wiki/ in combined sites (e.g. doc-forge with lib + wiki) # mcp -> MCP server on its configured port -# The docs homepage lists a repo once per declared kind (lib, api, wiki). +# The docs homepage shows one card per repo, grouped by its `section` +# (apps, tutorial, services, libraries); card links cover the repo's kinds. service: name: aetos-docs @@ -24,6 +25,7 @@ repos: source: ../openapi-first title: OpenAPI First description: Design-first API development toolkit enforcing OpenAPI contracts as the single source of truth. + section: libraries docs: lib: site mcp: { bundle: docs/mcp, server: openapi_first, port: 8004 } @@ -32,6 +34,7 @@ repos: source: ../doc-forge title: Doc Forge description: Renderer-agnostic Python documentation compiler powering MkDocs and MCP generation across Aetoskia projects. + section: libraries docs: wiki: site lib: site @@ -41,6 +44,7 @@ repos: source: ../dagpipe title: DAG Pipe description: Deterministic pipeline framework for executing state transformations through a directed acyclic graph (DAG). + section: libraries docs: lib: site mcp: { bundle: docs/mcp, server: dagpipe, port: 8006 } @@ -49,6 +53,7 @@ repos: source: ../mail-intake title: Mail Intake description: High-performance email ingestion and processing framework with pluggable adapters and automation workflows. + section: libraries docs: lib: site mcp: { bundle: docs/mcp, server: mail_intake, port: 8002 } @@ -57,6 +62,7 @@ repos: source: ../omniread title: Omniread description: Unified ingestion and normalization layer for structured and unstructured data sources. + section: libraries docs: lib: site mcp: { bundle: docs/mcp, server: omniread, port: 8003 } @@ -65,6 +71,7 @@ repos: source: ../py-jwt title: Py JWT description: Lightweight, explicit JWT utilities for Python services with strong validation and minimal magic. + section: libraries docs: lib: site mcp: { bundle: docs/mcp, server: jwtlib, port: 8005 } @@ -73,6 +80,7 @@ repos: source: ../hexa title: Hexa description: Hexagonal architecture framework and toolbox for Python services. + section: libraries docs: lib: site @@ -80,6 +88,7 @@ repos: source: ../mongo-ops title: Mongo Ops description: A modular, async MongoDB operations layer for FastAPI microservices. Simplifies CRUD, transactions, and model management. + section: libraries docs: wiki: site @@ -87,6 +96,7 @@ repos: source: ../auth-server title: Auth Server description: Aetoskia authentication service. OpenAPI-driven, FastAPI backend for auth flows. + section: services docs: api: site @@ -95,10 +105,12 @@ static: repo: blog title: Blog API description: A modular, async Blog API backend for FastAPI microservices. Provides streamlined CRUD operations for articles and authors. + section: apps path: blog - kind: tutorial repo: media-manager title: Media Manager Tutorial description: Step-by-step guide to the Media Manager architecture, ingestion pipeline, automation workflows, and integrations. + section: tutorial path: media-manager \ No newline at end of file