From b90634e8210817752c29021532895d1c972942e4 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 29 Apr 2026 14:15:08 +0200 Subject: [PATCH 1/6] Improve isolated build UX and fix git worktree scoped filesystem Header: light grey gradient with bottom border and shadow, replacing the blue background. GitHub link and deployment-info buttons share a muted grey gradient style with blue hover, using the system monospace font stack. Logo hover uses a subtle dark tint with consistent vertical padding. Nav: index page now surfaces as the first nav item using navigation_title from YAML front matter. Single-item breadcrumbs are hidden (no navigation value). Breadcrumb vertical padding reduced from py-6 to pt-4/pb-2. Sidebar: version selector, report-a-docs-issue, and learn-how-to-contribute are now gated on PrimaryNavEnabled so they don't show in isolated builds. Bug fixes: root page View-as-Markdown URL was .md, now correctly /index.md. h1 gains mt-6 top margin. FileSystemFactory.RealGitRootForPath adds the main repo's .git directory to scope roots when running from a git worktree, fixing ScopedFileSystemException when serving docs from a worktree checkout. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../FileSystemFactory.cs | 24 +++++- .../Assets/markdown/typography.css | 2 +- .../web-components/Header/DeploymentInfo.tsx | 81 +++++++++++-------- .../Assets/web-components/Header/Header.tsx | 40 ++++++++- .../Navigation/_TocTree.cshtml | 7 +- .../Navigation/_TocTreeNav.cshtml | 13 +++ src/Elastic.Markdown/HtmlWriter.cs | 2 +- .../Layout/_Breadcrumbs.cshtml | 5 +- .../Layout/_TableOfContents.cshtml | 6 +- 9 files changed, 129 insertions(+), 51 deletions(-) diff --git a/src/Elastic.Documentation.Configuration/FileSystemFactory.cs b/src/Elastic.Documentation.Configuration/FileSystemFactory.cs index 50870d5d53..6bcbd362bf 100644 --- a/src/Elastic.Documentation.Configuration/FileSystemFactory.cs +++ b/src/Elastic.Documentation.Configuration/FileSystemFactory.cs @@ -174,10 +174,28 @@ public static ScopedFileSystem ScopeSourceDirectoryForWrite(IFileSystem inner, s /// public static ScopedFileSystem RealGitRootForPath(string? path) { - if (path is null) + var root = path is null ? Paths.WorkingDirectoryRoot.FullName : Paths.FindGitRoot(path); + var roots = new List { root, Paths.ApplicationData.FullName }; + + // In a git worktree the local .git entry is a file pointing to the main repo's + // .git/worktrees/ directory. GitCheckoutInformation needs to read config and + // HEAD from the main repo's .git dir, which lives outside the worktree root. + // Add it as an explicit scope root so those reads are not rejected. + var worktreePointer = Path.Join(root, ".git"); + if (File.Exists(worktreePointer)) + { + var gitdir = File.ReadAllText(worktreePointer).Replace("gitdir:", "").Trim(); + // gitdir = /main/repo/.git/worktrees/ — go up two levels to reach .git + var mainGitDir = Path.GetFullPath(Path.Join(gitdir, "..", "..")); + if (Directory.Exists(mainGitDir)) + roots.Add(mainGitDir); + } + + // Fast path: no worktree detected and path was null — reuse the pre-built instance + if (roots.Count == 2 && path is null) return RealRead; - var root = Paths.FindGitRoot(path); - return new ScopedFileSystem(new FileSystem(), new ScopedFileSystemOptions([root, Paths.ApplicationData.FullName]) + + return new ScopedFileSystem(new FileSystem(), new ScopedFileSystemOptions([.. roots]) { AllowedHiddenFolderNames = new HashSet(StringComparer.OrdinalIgnoreCase) { ".git", ".artifacts" }, AllowedHiddenFileNames = new HashSet(StringComparer.OrdinalIgnoreCase) { ".git", ".doc.state" } diff --git a/src/Elastic.Documentation.Site/Assets/markdown/typography.css b/src/Elastic.Documentation.Site/Assets/markdown/typography.css index 71a402b105..3ef3a98968 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/typography.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/typography.css @@ -2,7 +2,7 @@ @apply text-ink font-body text-base text-pretty; h1 { - @apply text-ink-dark text-5xl font-semibold; + @apply text-ink-dark mt-6 text-5xl font-semibold; line-height: 1.2em; } diff --git a/src/Elastic.Documentation.Site/Assets/web-components/Header/DeploymentInfo.tsx b/src/Elastic.Documentation.Site/Assets/web-components/Header/DeploymentInfo.tsx index b9febf5d54..41a3966c91 100644 --- a/src/Elastic.Documentation.Site/Assets/web-components/Header/DeploymentInfo.tsx +++ b/src/Elastic.Documentation.Site/Assets/web-components/Header/DeploymentInfo.tsx @@ -12,11 +12,36 @@ import { useGeneratedHtmlId, IconType, EuiThemeComputed, - EuiButton, } from '@elastic/eui' import { css } from '@emotion/react' import { useState } from 'react' +export const headerButtonCss = (euiTheme: EuiThemeComputed) => css` + background: linear-gradient(to bottom, #f5f7fa 0%, #ffffff 100%); + border: 1px solid ${euiTheme.colors.lightShade}; + border-radius: ${euiTheme.border.radius.small}; + color: ${euiTheme.colors.ink}; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + 'Liberation Mono', 'Courier New', monospace; + font-size: 14px; + font-weight: 400; + padding: ${euiTheme.size.xs} ${euiTheme.size.m}; + cursor: pointer; + display: inline-flex; + align-items: center; + gap: ${euiTheme.size.s}; + text-decoration: none; + transition: + background 0.15s ease, + border-color 0.15s ease, + color 0.15s ease; + &:hover { + background: ${euiTheme.colors.primary}; + border-color: ${euiTheme.colors.primary}; + color: white; + } +` + interface DeploymentInfoProps { gitBranch: string gitCommit: string @@ -43,45 +68,34 @@ export const DeploymentInfo = ({ const popoverButton = ( - setIsOpen((prev) => !prev)} css={css` + ${headerButtonCss(euiTheme)}; margin-inline: ${euiTheme.size.s}; - font-family: ${euiTheme.font.familyCode}; `} > -
+ + {gitBranch} + + - - - {gitBranch} - - - - {gitCommit} - -
-
+ + {gitCommit} + +
) @@ -181,7 +195,8 @@ const DeploymentInfoRow = ({ size="xs" css={css` color: ${euiTheme.colors.textInk}; - font-family: ${euiTheme.font.familyCode}; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, + Consolas, 'Liberation Mono', 'Courier New', monospace; `} > {value} diff --git a/src/Elastic.Documentation.Site/Assets/web-components/Header/Header.tsx b/src/Elastic.Documentation.Site/Assets/web-components/Header/Header.tsx index e9a710874f..644b23ba79 100644 --- a/src/Elastic.Documentation.Site/Assets/web-components/Header/Header.tsx +++ b/src/Elastic.Documentation.Site/Assets/web-components/Header/Header.tsx @@ -1,9 +1,10 @@ import '../../eui-icons-cache' import { useHtmxContainer } from '../shared/htmx/useHtmxContainer' -import { DeploymentInfo } from './DeploymentInfo' +import { DeploymentInfo, headerButtonCss } from './DeploymentInfo' import { EuiHeader, EuiHeaderLogo, + EuiIcon, EuiProvider, useEuiTheme, } from '@elastic/eui' @@ -28,6 +29,7 @@ export const Header = ({ title, logoHref, githubRepository, + githubLink, gitBranch, gitCommit, githubRef, @@ -45,7 +47,13 @@ export const Header = ({ > span { - color: var(--color-white); + color: ${euiTheme.colors.ink}; } `} > @@ -68,6 +83,25 @@ export const Header = ({ ? [ { items: [ + ...(githubLink + ? [ + + + GitHub + , + ] + : []), *@ - @* @Model.Title *@ - @* *@ + @* index page surfaces as the first nav item via _TocTreeNav *@ } diff --git a/src/Elastic.Documentation.Site/Navigation/_TocTreeNav.cshtml b/src/Elastic.Documentation.Site/Navigation/_TocTreeNav.cshtml index ae8928c918..2fc661e4a0 100644 --- a/src/Elastic.Documentation.Site/Navigation/_TocTreeNav.cshtml +++ b/src/Elastic.Documentation.Site/Navigation/_TocTreeNav.cshtml @@ -6,6 +6,19 @@ @{ var isTopLevel = Model.Level == 0; } +@if (isTopLevel) +{ + var idx = Model.SubTree.Index; +
  • + + @idx.NavigationTitle + +
  • +} @foreach (var item in Model.SubTree.NavigationItems) { if (item.Hidden) diff --git a/src/Elastic.Markdown/HtmlWriter.cs b/src/Elastic.Markdown/HtmlWriter.cs index cd1d29ccf0..b2bcfa7e05 100644 --- a/src/Elastic.Markdown/HtmlWriter.cs +++ b/src/Elastic.Markdown/HtmlWriter.cs @@ -162,7 +162,7 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc SiteRootPath = DocumentationSet.Context.SiteRootPath, AppliesTo = markdown.YamlFrontMatter?.AppliesTo, GithubEditUrl = editUrl, - MarkdownUrl = current.Url.TrimEnd('/') + ".md", + MarkdownUrl = current.Url == "/" ? "/index.md" : current.Url.TrimEnd('/') + ".md", AllowIndexing = DocumentationSet.Context.AllowIndexing && markdown.YamlFrontMatter?.NoIndex != true && (markdown.CrossLink.Equals("docs-content://index.md", StringComparison.OrdinalIgnoreCase) || markdown is DetectionRuleFile || !current.Hidden), CanonicalBaseUrl = DocumentationSet.Context.CanonicalBaseUrl, GoogleTagManager = DocumentationSet.Context.GoogleTagManager, diff --git a/src/Elastic.Markdown/Layout/_Breadcrumbs.cshtml b/src/Elastic.Markdown/Layout/_Breadcrumbs.cshtml index 61de6bd6a1..ea99baab1e 100644 --- a/src/Elastic.Markdown/Layout/_Breadcrumbs.cshtml +++ b/src/Elastic.Markdown/Layout/_Breadcrumbs.cshtml @@ -4,7 +4,9 @@ var crumbs = targets.ToList(); } -