From 83a6b635d59ea5503719625dbb141f018fba6d62 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Wed, 27 May 2026 22:36:34 +0200 Subject: [PATCH] Assembler: embed elastic-website-search web component on staging Adds the elastic-website-search web component (chat-mode="input") to the assembler build, gated behind a new WEBSITE_SEARCH feature flag. The script URL is configurable per environment via website_search_url in assembler.yml, so staging and local dev can each point at their own bundle. The chat input mounts into a sticky container at the bottom of the content column with a short entrance animation. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- config/assembler.yml | 4 ++++ .../Assembler/PublishEnvironment.cs | 3 +++ .../Builder/FeatureFlags.cs | 8 ++++++++ .../Assets/assembler.css | 15 +++++++++++++++ .../Layout/_Head.cshtml | 4 ++++ src/Elastic.Markdown/_Layout.cshtml | 15 +++++++++++++++ .../Building/AssemblerBuilder.cs | 1 + 7 files changed, 50 insertions(+) diff --git a/config/assembler.yml b/config/assembler.yml index 386495e599..baefcb9bb7 100644 --- a/config/assembler.yml +++ b/config/assembler.yml @@ -24,6 +24,8 @@ environments: cookies_win: x feature_flags: SEARCH_OR_ASK_AI: true + WEBSITE_SEARCH: true + website_search_url: https://staging-website.elastic.co/search/elastic-website-search.js edge: uri: https://d34ipnu52o64md.cloudfront.net path_prefix: docs @@ -38,6 +40,8 @@ environments: path_prefix: docs feature_flags: SEARCH_OR_ASK_AI: true + WEBSITE_SEARCH: true + website_search_url: http://localhost:4078/elastic-website-search.js preview: uri: https://docs-v3-preview.elastic.dev path_prefix: ${ASSEMBLER_PREVIEW_PATH_PREFIX} diff --git a/src/Elastic.Documentation.Configuration/Assembler/PublishEnvironment.cs b/src/Elastic.Documentation.Configuration/Assembler/PublishEnvironment.cs index b581448f3c..add8188822 100644 --- a/src/Elastic.Documentation.Configuration/Assembler/PublishEnvironment.cs +++ b/src/Elastic.Documentation.Configuration/Assembler/PublishEnvironment.cs @@ -31,4 +31,7 @@ public record PublishEnvironment [YamlMember(Alias = "feature_flags")] public Dictionary FeatureFlags { get; set; } = []; + + [YamlMember(Alias = "website_search_url")] + public string? WebsiteSearchScriptUrl { get; set; } } diff --git a/src/Elastic.Documentation.Configuration/Builder/FeatureFlags.cs b/src/Elastic.Documentation.Configuration/Builder/FeatureFlags.cs index 3edfad1fc8..bcea10422d 100644 --- a/src/Elastic.Documentation.Configuration/Builder/FeatureFlags.cs +++ b/src/Elastic.Documentation.Configuration/Builder/FeatureFlags.cs @@ -38,6 +38,14 @@ public bool StagingElasticNavEnabled set => _featureFlags["staging-elastic-nav"] = value; } + public bool WebsiteSearchEnabled + { + get => IsEnabled("website-search"); + set => _featureFlags["website-search"] = value; + } + + public string? WebsiteSearchScriptUrl { get; set; } + public bool AirGappedEnabled { get => IsEnabled("air-gapped"); diff --git a/src/Elastic.Documentation.Site/Assets/assembler.css b/src/Elastic.Documentation.Site/Assets/assembler.css index b99630104e..73cf647e6e 100644 --- a/src/Elastic.Documentation.Site/Assets/assembler.css +++ b/src/Elastic.Documentation.Site/Assets/assembler.css @@ -21,6 +21,21 @@ body.air-gapped elastic-docs-header .euiHeader { padding-inline: calc((100vw - var(--max-layout-width)) / 2 + 8px); } +@keyframes website-search-enter { + from { + opacity: 0; + translate: 0 1rem; + } + to { + opacity: 1; + translate: 0 0; + } +} + +#elastic-website-search-input-container { + animation: website-search-enter 0.2s ease-out 0.6s both; +} + #elastic-nav { @media screen and (min-width: 1200px) { [data-component='Container'] { diff --git a/src/Elastic.Documentation.Site/Layout/_Head.cshtml b/src/Elastic.Documentation.Site/Layout/_Head.cshtml index dcdcf3df7d..25cdd4c424 100644 --- a/src/Elastic.Documentation.Site/Layout/_Head.cshtml +++ b/src/Elastic.Documentation.Site/Layout/_Head.cshtml @@ -27,6 +27,10 @@ } + @if (Model.Features.WebsiteSearchEnabled && Model.Features.WebsiteSearchScriptUrl is { } websiteSearchUrl) + { + + } @if (Model.CanonicalBaseUrl is not null) { diff --git a/src/Elastic.Markdown/_Layout.cshtml b/src/Elastic.Markdown/_Layout.cshtml index 098ddc337a..88b1df2443 100644 --- a/src/Elastic.Markdown/_Layout.cshtml +++ b/src/Elastic.Markdown/_Layout.cshtml @@ -47,6 +47,12 @@ @await RenderBodyAsync() @await RenderPartialAsync(_PrevNextNav.Create(Model)) + @if (Model.Features.WebsiteSearchEnabled && Model.Features.WebsiteSearchScriptUrl is not null) + { +
+
+ } @await RenderPartialAsync(_TableOfContents.Create(Model)) @@ -89,6 +95,15 @@ break; } +@if (Model.Features.WebsiteSearchEnabled && Model.Features.WebsiteSearchScriptUrl is not null) +{ + + +} @if (RenderHeaderAndFooter) { if (Model.BuildType == BuildType.Assembler) diff --git a/src/services/Elastic.Documentation.Assembler/Building/AssemblerBuilder.cs b/src/services/Elastic.Documentation.Assembler/Building/AssemblerBuilder.cs index 430abe212e..4a7c0d61db 100644 --- a/src/services/Elastic.Documentation.Assembler/Building/AssemblerBuilder.cs +++ b/src/services/Elastic.Documentation.Assembler/Building/AssemblerBuilder.cs @@ -180,6 +180,7 @@ private void SetFeatureFlags(AssemblerDocumentationSet set) _logger.LogInformation("Setting feature flag: {ConfigurationFeatureFlagKey}={ConfigurationFeatureFlagValue}", configurationFeatureFlag.Key, configurationFeatureFlag.Value); set.DocumentationSet.Configuration.Features.Set(configurationFeatureFlag.Key, configurationFeatureFlag.Value); } + set.DocumentationSet.Configuration.Features.WebsiteSearchScriptUrl = set.AssembleContext.Environment.WebsiteSearchScriptUrl; } private void LogBuildTimes(List<(string Name, int FileCount, TimeSpan Duration)> buildTimes)