From 0386e75a2b4d0e3dc63c0cebc18b2ee7f23f4cfc Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Wed, 17 Jun 2026 09:42:35 +0200 Subject: [PATCH 1/2] Codex: sort landing sections alphabetically and redesign cards - Sort groups and ungrouped doc sets together by title so the grid is consistent regardless of config order - Expand group cards into a header + inner grid: each group shows its doc sets inline, with adaptive column span based on repo count - Compact card layout: smaller icon, tighter padding, description clamped to 2 lines - Add RepoPath property to CodexCardModel and render it as a monospace subtitle (e.g. /r/beacon) so users can find doc sets via ctrl+F Co-Authored-By: Claude Sonnet 4.6 --- .../Group/GroupLandingView.cshtml | 3 +- src/Elastic.Codex/Landing/CodexCardModel.cs | 6 ++ src/Elastic.Codex/Landing/LandingView.cshtml | 75 ++++++++++++++----- src/Elastic.Codex/Landing/_CodexCard.cshtml | 32 ++++---- 4 files changed, 81 insertions(+), 35 deletions(-) diff --git a/src/Elastic.Codex/Group/GroupLandingView.cshtml b/src/Elastic.Codex/Group/GroupLandingView.cshtml index c7bce688d4..155f7fff26 100644 --- a/src/Elastic.Codex/Group/GroupLandingView.cshtml +++ b/src/Elastic.Codex/Group/GroupLandingView.cshtml @@ -25,7 +25,8 @@ Title = docSet.Title ?? docSet.Name, Description = docSet.Description, Icon = docSet.Icon, - PageCount = docSet.PageCount + PageCount = docSet.PageCount, + RepoPath = $"/r/{docSet.Name}" }))) } diff --git a/src/Elastic.Codex/Landing/CodexCardModel.cs b/src/Elastic.Codex/Landing/CodexCardModel.cs index e1d448288b..50fc2d21cf 100644 --- a/src/Elastic.Codex/Landing/CodexCardModel.cs +++ b/src/Elastic.Codex/Landing/CodexCardModel.cs @@ -14,4 +14,10 @@ public record CodexCardModel public string? Description { get; init; } public string? Icon { get; init; } public int PageCount { get; init; } + + /// + /// The repo path (e.g. "/r/beacon") shown on docset cards so it is findable via browser ctrl+F. Null for group cards. + /// + public string? RepoPath { get; init; } + } diff --git a/src/Elastic.Codex/Landing/LandingView.cshtml b/src/Elastic.Codex/Landing/LandingView.cshtml index 3b1486dcc7..365c819b1f 100644 --- a/src/Elastic.Codex/Landing/LandingView.cshtml +++ b/src/Elastic.Codex/Landing/LandingView.cshtml @@ -123,28 +123,67 @@ + @{ + var sortedSections = Model.Groups + .Select(g => (sortKey: g.DisplayTitle, group: (GroupNavigation?)g, docSet: (CodexDocumentationSetInfo?)null)) + .Concat(Model.UngroupedDocumentationSets + .Select(ds => (sortKey: ds.Title ?? ds.Name, group: (GroupNavigation?)null, docSet: (CodexDocumentationSetInfo?)ds))) + .OrderBy(x => x.sortKey, StringComparer.OrdinalIgnoreCase) + .ToList(); + }
- @foreach (var group in Model.Groups) + @foreach (var item in sortedSections) { - @(await RenderPartialAsync(_CodexCard.Create(new CodexCardModel + @if (item.group is { } group) { - Url = group.Url, - Title = group.DisplayTitle, - Description = group.Description, - Icon = group.Icon, - PageCount = group.DocumentationSetInfos.Sum(ds => ds.PageCount) - }))) - } - @foreach (var docSet in Model.UngroupedDocumentationSets) - { - @(await RenderPartialAsync(_CodexCard.Create(new CodexCardModel + var repoCount = group.DocumentationSetInfos.Count; + var colSpan = repoCount >= 5 || repoCount == 3 ? "col-span-full" : repoCount >= 2 ? "col-span-1 md:col-span-2" : "col-span-1"; + var innerCols = repoCount >= 5 || repoCount == 3 ? "grid-cols-1 md:grid-cols-2 lg:grid-cols-3" : repoCount >= 2 ? "grid-cols-1 md:grid-cols-2" : "grid-cols-1"; +
+ +
+ @GetIconSvg(group.Icon, "w-6 h-6 fill-current") +
+
+
@group.DisplayTitle
+
/g/@group.GroupSlug
+ @if (!string.IsNullOrEmpty(group.Description)) + { +

@group.Description

+ } +
+
+
+ @foreach (var docSet in group.DocumentationSetInfos.OrderBy(ds => ds.Title)) + { + @(await RenderPartialAsync(_CodexCard.Create(new CodexCardModel + { + Url = docSet.Url, + Title = docSet.Title ?? docSet.Name, + Description = docSet.Description, + Icon = docSet.Icon, + PageCount = docSet.PageCount, + RepoPath = $"/r/{docSet.Name}" + }))) + } +
+
+ } + else if (item.docSet is { } docSet) { - Url = docSet.Url, - Title = docSet.Title ?? docSet.Name, - Description = docSet.Description, - Icon = docSet.Icon, - PageCount = docSet.PageCount - }))) + @(await RenderPartialAsync(_CodexCard.Create(new CodexCardModel + { + Url = docSet.Url, + Title = docSet.Title ?? docSet.Name, + Description = docSet.Description, + Icon = docSet.Icon, + PageCount = docSet.PageCount, + RepoPath = $"/r/{docSet.Name}" + }))) + } }
diff --git a/src/Elastic.Codex/Landing/_CodexCard.cshtml b/src/Elastic.Codex/Landing/_CodexCard.cshtml index 8c2a3e6a5a..0ec6bbeb04 100644 --- a/src/Elastic.Codex/Landing/_CodexCard.cshtml +++ b/src/Elastic.Codex/Landing/_CodexCard.cshtml @@ -11,25 +11,25 @@ -
-
- @GetIconSvg(Model.Icon, "w-6 h-6 fill-current") +
+
+ @GetIconSvg(Model.Icon, "w-4 h-4 fill-current") +
+
+
@Model.Title
+ @if (!string.IsNullOrEmpty(Model.RepoPath)) + { +
@Model.RepoPath
+ }
-
- @Model.Title -
-
- @if (!string.IsNullOrEmpty(Model.Description)) - { -
- @Model.Description -
- } -
-
+ @if (!string.IsNullOrEmpty(Model.Description)) + { +
@Model.Description
+ } +
@GetIconSvg("documents", "fill-current size-3") @Model.PageCount pages
From e4f0d1c7387417745fe0218d1e229252794d83db Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Wed, 17 Jun 2026 10:08:19 +0200 Subject: [PATCH 2/2] Codex: centralize docset card mapping and repo path Hoist the CodexDocumentationSetInfo to CodexCardModel mapping into one factory so every docset card renders the same fields, fixing top-level docset cards that had silently dropped the /r/... subtitle. Make RepoPath a computed property instead of redundant stored state, and reuse the docset URL instead of rebuilding the /r/{repo} segment three times. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Group/GroupLandingView.cshtml | 10 +--------- src/Elastic.Codex/Landing/CodexCardModel.cs | 14 +++++++++++++ src/Elastic.Codex/Landing/LandingView.cshtml | 20 ++----------------- .../Navigation/CodexIndexPage.cs | 5 +++++ .../Navigation/CodexNavigation.cs | 5 ++--- 5 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/Elastic.Codex/Group/GroupLandingView.cshtml b/src/Elastic.Codex/Group/GroupLandingView.cshtml index 155f7fff26..f171312a68 100644 --- a/src/Elastic.Codex/Group/GroupLandingView.cshtml +++ b/src/Elastic.Codex/Group/GroupLandingView.cshtml @@ -19,15 +19,7 @@
@foreach (var docSet in Model.DocumentationSets.OrderBy(ds => ds.Title)) { - @(await RenderPartialAsync(_CodexCard.Create(new CodexCardModel - { - Url = docSet.Url, - Title = docSet.Title ?? docSet.Name, - Description = docSet.Description, - Icon = docSet.Icon, - PageCount = docSet.PageCount, - RepoPath = $"/r/{docSet.Name}" - }))) + @(await RenderPartialAsync(_CodexCard.Create(CodexCardModel.FromDocumentationSet(docSet)))) }
diff --git a/src/Elastic.Codex/Landing/CodexCardModel.cs b/src/Elastic.Codex/Landing/CodexCardModel.cs index 50fc2d21cf..44ea879dc3 100644 --- a/src/Elastic.Codex/Landing/CodexCardModel.cs +++ b/src/Elastic.Codex/Landing/CodexCardModel.cs @@ -2,6 +2,8 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using Elastic.Codex.Navigation; + namespace Elastic.Codex.Landing; /// @@ -20,4 +22,16 @@ public record CodexCardModel /// public string? RepoPath { get; init; } + /// + /// Builds the card model for a documentation set, so every docset card renders the same fields. + /// + public static CodexCardModel FromDocumentationSet(CodexDocumentationSetInfo docSet) => new() + { + Url = docSet.Url, + Title = docSet.Title ?? docSet.Name, + Description = docSet.Description, + Icon = docSet.Icon, + PageCount = docSet.PageCount, + RepoPath = docSet.RepoPath, + }; } diff --git a/src/Elastic.Codex/Landing/LandingView.cshtml b/src/Elastic.Codex/Landing/LandingView.cshtml index 365c819b1f..50fa08e9e6 100644 --- a/src/Elastic.Codex/Landing/LandingView.cshtml +++ b/src/Elastic.Codex/Landing/LandingView.cshtml @@ -159,30 +159,14 @@
@foreach (var docSet in group.DocumentationSetInfos.OrderBy(ds => ds.Title)) { - @(await RenderPartialAsync(_CodexCard.Create(new CodexCardModel - { - Url = docSet.Url, - Title = docSet.Title ?? docSet.Name, - Description = docSet.Description, - Icon = docSet.Icon, - PageCount = docSet.PageCount, - RepoPath = $"/r/{docSet.Name}" - }))) + @(await RenderPartialAsync(_CodexCard.Create(CodexCardModel.FromDocumentationSet(docSet)))) }
} else if (item.docSet is { } docSet) { - @(await RenderPartialAsync(_CodexCard.Create(new CodexCardModel - { - Url = docSet.Url, - Title = docSet.Title ?? docSet.Name, - Description = docSet.Description, - Icon = docSet.Icon, - PageCount = docSet.PageCount, - RepoPath = $"/r/{docSet.Name}" - }))) + @(await RenderPartialAsync(_CodexCard.Create(CodexCardModel.FromDocumentationSet(docSet)))) } }
diff --git a/src/Elastic.Codex/Navigation/CodexIndexPage.cs b/src/Elastic.Codex/Navigation/CodexIndexPage.cs index e4d68b3390..e337defa1e 100644 --- a/src/Elastic.Codex/Navigation/CodexIndexPage.cs +++ b/src/Elastic.Codex/Navigation/CodexIndexPage.cs @@ -38,6 +38,11 @@ public record CodexDocumentationSetInfo /// public required string Url { get; init; } + /// + /// The repo-scoped path segment (e.g. "/r/beacon"), derived from . + /// + public string RepoPath => $"/r/{Name}"; + /// /// The group id this documentation set belongs to, if any. /// diff --git a/src/Elastic.Codex/Navigation/CodexNavigation.cs b/src/Elastic.Codex/Navigation/CodexNavigation.cs index a49df4f814..c3b75fab4c 100644 --- a/src/Elastic.Codex/Navigation/CodexNavigation.cs +++ b/src/Elastic.Codex/Navigation/CodexNavigation.cs @@ -97,14 +97,13 @@ private void ProcessDocumentationSet(CodexDocumentationSetReference docSetRef) if (docSetNav is not IRootNavigationItem rootNavItem) return; - var pathPrefix = $"{codex.Url}/r/{repoName}"; var docSetInfo = CreateDocumentationSetInfo(docSetRef, rootNavItem, repoName); _docSetInfos.Add(docSetInfo); if (!string.IsNullOrEmpty(docSetRef.Group)) - AttachToGroup(docSetRef, docSetNav, rootNavItem, pathPrefix, docSetInfo); + AttachToGroup(docSetRef, docSetNav, rootNavItem, docSetInfo.Url, docSetInfo); else - AttachToCodexRoot(docSetNav, rootNavItem, pathPrefix); + AttachToCodexRoot(docSetNav, rootNavItem, docSetInfo.Url); } private CodexDocumentationSetInfo CreateDocumentationSetInfo(