diff --git a/config/assembler.yml b/config/assembler.yml index 386495e599..c3e0d2382b 100644 --- a/config/assembler.yml +++ b/config/assembler.yml @@ -38,6 +38,7 @@ environments: path_prefix: docs feature_flags: SEARCH_OR_ASK_AI: true + NAV_V2: true preview: uri: https://docs-v3-preview.elastic.dev path_prefix: ${ASSEMBLER_PREVIEW_PATH_PREFIX} @@ -46,6 +47,7 @@ environments: enabled: false feature_flags: SEARCH_OR_ASK_AI: true + NAV_V2: true air-gapped: uri: http://localhost:8080 path_prefix: docs @@ -71,6 +73,7 @@ shared_configuration: ### narrative: checkout_strategy: full + current: hub-pages ### # 'references' defines a map of `elastic/ * diff --git a/config/navigation-v2.yml b/config/navigation-v2.yml new file mode 100644 index 0000000000..2a5ba28762 --- /dev/null +++ b/config/navigation-v2.yml @@ -0,0 +1,4489 @@ +nav: + - section: Guides + url: / + children: + - label: Elasticsearch fundamentals + children: + - group: Get started in seconds + page: docs-content://get-started/index.md + children: + - page: docs-content://get-started/evaluate-elastic.md + title: Evaluate Elastic during a trial + - group: Elasticsearch concepts + children: + - page: docs-content://manage-data/data-store.md + title: Elasticsearch essentials (Core document database) + - page: docs-content://manage-data/ingest.md + title: Ingest data into Elasticsearch + - page: docs-content://solutions/search.md + title: Core search features + - page: docs-content://explore-analyze/index.md + title: Visualize and analyze (Kibana) + - group: Use cases + children: + - page: docs-content://explore-analyze/geospatial-analysis.md + title: Geospatial analysis + - page: docs-content://solutions/search/site-or-app.md + title: Add search to your site or app + - page: docs-content://solutions/search/rag.md + title: RAG + - label: Install, deploy, and administer + children: + - group: Distributed architecture + page: docs-content://deploy-manage/distributed-architecture.md + children: + - group: Clusters, nodes, and shards + page: docs-content://deploy-manage/distributed-architecture/clusters-nodes-shards.md + children: + - page: docs-content://deploy-manage/distributed-architecture/clusters-nodes-shards/node-roles.md + title: Node roles + - page: docs-content://deploy-manage/distributed-architecture/reading-and-writing-documents.md + title: Reading and writing documents + - group: Shard allocation, relocation, and recovery + page: docs-content://deploy-manage/distributed-architecture/shard-allocation-relocation-recovery.md + children: + - page: docs-content://deploy-manage/distributed-architecture/shard-allocation-relocation-recovery/shard-allocation-awareness.md + title: Shard allocation awareness + - group: Index-level shard allocation filtering + page: docs-content://deploy-manage/distributed-architecture/shard-allocation-relocation-recovery/index-level-shard-allocation.md + children: + - page: docs-content://deploy-manage/distributed-architecture/shard-allocation-relocation-recovery/delaying-allocation-when-node-leaves.md + title: Delaying allocation when a node leaves + - page: docs-content://deploy-manage/distributed-architecture/shard-request-cache.md + title: The shard request cache + - group: Discovery and cluster formation + page: docs-content://deploy-manage/distributed-architecture/discovery-cluster-formation.md + children: + - page: docs-content://deploy-manage/distributed-architecture/discovery-cluster-formation/discovery-hosts-providers.md + title: Discovery hosts providers + - page: docs-content://deploy-manage/distributed-architecture/discovery-cluster-formation/modules-discovery-quorums.md + title: Quorum-based decision making + - page: docs-content://deploy-manage/distributed-architecture/discovery-cluster-formation/modules-discovery-voting.md + title: Voting configurations + - page: docs-content://deploy-manage/distributed-architecture/discovery-cluster-formation/modules-discovery-bootstrap-cluster.md + title: Bootstrapping a cluster + - page: docs-content://deploy-manage/distributed-architecture/discovery-cluster-formation/cluster-state-overview.md + title: Cluster state + - page: docs-content://deploy-manage/distributed-architecture/discovery-cluster-formation/cluster-fault-detection.md + title: Cluster fault detection + - page: docs-content://deploy-manage/distributed-architecture/kibana-tasks-management.md + title: Kibana task management + - group: Plan your install + children: + - page: docs-content://deploy-manage/deploy/deployment-comparison.md + title: Deployment comparison + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/differences-from-other-elasticsearch-offerings.md + title: Compare Cloud Hosted and Serverless + - page: docs-content://get-started/versioning-availability.md + title: Versioning and compatibility + - group: Reference architectures + page: docs-content://deploy-manage/reference-architectures.md + children: + - page: docs-content://deploy-manage/reference-architectures/hotfrozen-high-availability.md + title: Hot/Frozen - High Availability + - page: docs-content://deploy-manage/reference-architectures/genai-search-high-availability.md + title: GenAI Search - High Availability + - group: Production guidance + page: docs-content://deploy-manage/production-guidance.md + children: + - group: Run Elasticsearch in production + page: docs-content://deploy-manage/production-guidance/elasticsearch-in-production-environments.md + children: + - group: Design for resilience + page: docs-content://deploy-manage/production-guidance/availability-and-resilience.md + children: + - page: docs-content://deploy-manage/production-guidance/availability-and-resilience/resilience-in-small-clusters.md + title: Resilience in small clusters + - page: docs-content://deploy-manage/production-guidance/availability-and-resilience/resilience-in-larger-clusters.md + title: Resilience in larger clusters + - page: docs-content://deploy-manage/production-guidance/availability-and-resilience/resilience-in-ech.md + title: Resilience in ECH and ECE + - page: docs-content://deploy-manage/production-guidance/scaling-considerations.md + title: Scaling considerations + - group: Performance optimizations + page: docs-content://deploy-manage/production-guidance/optimize-performance.md + children: + - page: docs-content://deploy-manage/production-guidance/general-recommendations.md + title: General recommendations + - page: docs-content://deploy-manage/production-guidance/optimize-performance/indexing-speed.md + title: Tune for indexing speed + - page: docs-content://deploy-manage/production-guidance/optimize-performance/search-speed.md + title: Tune for search speed + - page: docs-content://deploy-manage/production-guidance/optimize-performance/approximate-knn-search.md + title: Tune approximate kNN search + - page: docs-content://deploy-manage/production-guidance/optimize-performance/disk-usage.md + title: Tune for disk usage + - page: docs-content://deploy-manage/production-guidance/optimize-performance/size-shards.md + title: Size your shards + - group: Run Kibana in production + page: docs-content://deploy-manage/production-guidance/kibana-in-production-environments.md + children: + - page: docs-content://deploy-manage/production-guidance/kibana-load-balance-traffic.md + title: High availability and load balancing + - page: docs-content://deploy-manage/production-guidance/kibana-configure-memory.md + title: Configure memory + - page: docs-content://deploy-manage/production-guidance/kibana-task-manager-scaling-considerations.md + title: Manage background tasks + - page: docs-content://deploy-manage/production-guidance/kibana-traffic-scaling-considerations.md + title: Traffic scaling considerations + - page: docs-content://deploy-manage/production-guidance/kibana-alerting-production-considerations.md + title: Optimize alerting performance + - page: docs-content://deploy-manage/production-guidance/kibana-reporting-production-considerations.md + title: Reporting production considerations + - group: Install and deploy Elasticsearch + page: docs-content://deploy-manage/deploy.md + children: + - title: Skills for installation and deployment + - group: Deploy in Elastic Cloud + page: docs-content://deploy-manage/deploy/elastic-cloud.md + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/create-an-organization.md + title: Create an organization + children: + - group: Subscribe from a marketplace + page: docs-content://deploy-manage/deploy/elastic-cloud/subscribe-from-marketplace.md + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/aws-marketplace.md + title: AWS Marketplace + - page: docs-content://deploy-manage/deploy/elastic-cloud/google-cloud-platform-marketplace.md + title: Google Cloud Platform Marketplace + - page: docs-content://deploy-manage/deploy/elastic-cloud/azure-native-isv-service.md + title: Azure Native ISV Service + - group: Elasticsearch Add-On for Heroku + page: docs-content://deploy-manage/deploy/elastic-cloud/heroku.md + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/heroku-getting-started-installing.md + title: Install the add-on + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/heroku-getting-started-removing.md + title: Remove the add-on + - page: docs-content://deploy-manage/deploy/elastic-cloud/heroku-getting-started-accessing.md + title: Access the console + - page: docs-content://deploy-manage/deploy/elastic-cloud/heroku-working-with-elasticsearch.md + title: Work with Elasticsearch + - page: docs-content://deploy-manage/deploy/elastic-cloud/heroku-migrating.md + title: Migrate between plans + - page: docs-content://deploy-manage/deploy/elastic-cloud/heroku-reference-hardware.md + title: Hardware + - page: docs-content://deploy-manage/deploy/elastic-cloud/heroku-reference-regions.md + title: Regions + - group: Elastic Cloud Serverless + page: docs-content://deploy-manage/deploy/elastic-cloud/serverless.md + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/create-serverless-project.md + title: Create a serverless project + - page: docs-content://deploy-manage/deploy/elastic-cloud/regions.md + title: Regions + - title: Production readiness checklist + - group: Elastic Cloud Hosted + page: docs-content://deploy-manage/deploy/elastic-cloud/cloud-hosted.md + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/create-an-elastic-cloud-hosted-deployment.md + title: Create a deployment + - title: Connect to Elasticsearch + - page: docs-content://deploy-manage/deploy/elastic-cloud/access-kibana.md # BOTH PHASES + title: Access Kibana + - group: Prepare for production + page: docs-content://deploy-manage/deploy/elastic-cloud/elastic-cloud-hosted-planning.md + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/ec-customize-deployment-components.md # BOTH PHASES + title: Customize deployment components + - title: Production readiness checklist + - page: docs-content://deploy-manage/deploy/elastic-cloud/fedramp.md + title: FedRAMP authorized Cloud offerings + # - page: docs-content://deploy-manage/deploy/elastic-cloud/azure-marketplace-pricing.md + # title: Azure Marketplace pricing + # - page: docs-content://deploy-manage/deploy/elastic-cloud/create-monthly-pay-as-you-go-subscription-on-aws-marketplace.md + # title: Monthly AWS subscription + # - page: docs-content://deploy-manage/deploy/elastic-cloud/create-monthly-pay-as-you-go-subscription-on-gcp-marketplace.md + # title: Monthly GCP subscription + # - page: docs-content://deploy-manage/deploy/elastic-cloud/complete-registration-of-an-annual-subscription-on-aws-marketplace.md + # title: Annual AWS subscription + - group: Elastic Cloud Enterprise + page: docs-content://deploy-manage/deploy/cloud-enterprise.md + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-architecture.md + title: Service-oriented architecture + - group: Prepare your environment + page: docs-content://deploy-manage/deploy/cloud-enterprise/prepare-environment.md + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/identify-deployment-scenario.md + title: Identify your deployment scenario + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-hardware-prereq.md + title: Hardware prerequisites + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-software-prereq.md + title: Software prerequisites + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-sysconfig.md + title: System configuration + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-networking-prereq.md + title: Networking prerequisites + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-users-permissions.md + title: Users and permissions + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-ha.md + title: High availability + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-roles.md + title: Separation of roles + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-jvm.md + title: JVM heap size + - group: Configure your operating system + page: docs-content://deploy-manage/deploy/cloud-enterprise/configure-operating-system.md + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/configure-host-ubuntu.md + title: Ubuntu + - page: docs-content://deploy-manage/deploy/cloud-enterprise/configure-host-rhel.md + title: RHEL + - page: docs-content://deploy-manage/deploy/cloud-enterprise/configure-host-suse.md + title: SUSE + - group: Install ECE + page: docs-content://deploy-manage/deploy/cloud-enterprise/install.md + children: + - group: Installation procedures + page: docs-content://deploy-manage/deploy/cloud-enterprise/install-ece-procedures.md + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/deploy-small-installation.md + title: Small installation + - page: docs-content://deploy-manage/deploy/cloud-enterprise/deploy-medium-installation.md + title: Medium installation + - page: docs-content://deploy-manage/deploy/cloud-enterprise/deploy-large-installation.md + title: Large installation + - page: docs-content://deploy-manage/deploy/cloud-enterprise/fresh-installation-of-ece-using-podman-hosts.md + title: Deploy using Podman + - page: docs-content://deploy-manage/deploy/cloud-enterprise/alternative-install-ece-with-ansible.md + title: Ansible playbook + - group: Air-gapped install + page: docs-content://deploy-manage/deploy/cloud-enterprise/air-gapped-install.md + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-install-offline-with-registry.md + title: With your private Docker registry + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-install-offline-no-registry.md + title: Without a private Docker registry + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-install-offline-images.md + title: Available Docker images + - page: docs-content://deploy-manage/deploy/cloud-enterprise/log-into-cloud-ui.md # BOTH PHASES + title: Log into the Cloud UI + - group: Post-installation + page: docs-content://deploy-manage/deploy/cloud-enterprise/post-installation-steps.md + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-wildcard-dns.md + title: Wildcard DNS and certificates + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-load-balancers.md + title: Load balancers + - page: docs-content://deploy-manage/deploy/cloud-enterprise/system-deployments-configuration.md # BOTH PHASES + title: System deployments configuration + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/default-system-deployment-versions.md + title: Default system deployment versions + - group: Create your first deployment + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/create-deployment.md # BOTH PHASES + title: Create a deployment + - page: docs-content://deploy-manage/deploy/cloud-enterprise/connect-elasticsearch.md # BOTH PHASES + title: Connect to Elasticsearch + - page: docs-content://deploy-manage/deploy/cloud-enterprise/access-kibana.md # BOTH PHASES + title: Access Kibana + - group: Elastic Cloud on Kubernetes + page: docs-content://deploy-manage/deploy/cloud-on-k8s.md + children: + - group: Deploy an orchestrator + page: docs-content://deploy-manage/deploy/cloud-on-k8s/install.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/install-using-yaml-manifest-quickstart.md + title: YAML manifests + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/install-using-helm-chart.md + title: Helm chart + - group: Deploy on OpenShift + page: docs-content://deploy-manage/deploy/cloud-on-k8s/deploy-eck-on-openshift.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-openshift-deploy-operator.md + title: Deploy the operator + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-openshift-deploy-elasticsearch.md + title: Deploy Elasticsearch + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-openshift-deploy-kibana.md + title: Deploy Kibana + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-openshift-anyuid-workaround.md + title: anyuid SCC workaround + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/deploy-eck-on-gke-autopilot.md + title: Deploy on GKE Autopilot + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/eck-gdch.md + title: Deploy on Google Distributed Cloud air-gapped + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/deploy-fips-compatible-version-of-eck.md + title: FIPS compatibility + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/air-gapped-install.md + title: Air-gapped environments + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/required-rbac-permissions.md # BOTH PHASES + title: Required RBAC permissions + - group: Deploy your first workloads + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/elasticsearch-deployment-quickstart.md + title: Deploy an Elasticsearch cluster + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/kibana-instance-quickstart.md + title: Deploy a Kibana instance + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/accessing-services.md # BOTH PHASES + title: Accessing services + - title: Production readiness checklist + - group: Self-managed cluster + page: docs-content://deploy-manage/deploy/self-managed.md + children: + - group: Install Elasticsearch + page: docs-content://deploy-manage/deploy/self-managed/installing-elasticsearch.md + children: + - page: docs-content://deploy-manage/deploy/self-managed/local-development-installation-quickstart.md + title: Local installation (quickstart) + - group: Important system configuration + page: docs-content://deploy-manage/deploy/self-managed/important-system-configuration.md + children: + - page: docs-content://deploy-manage/deploy/self-managed/elasticsearch-service-user.md + title: Elasticsearch service user + - page: docs-content://deploy-manage/deploy/self-managed/setting-system-settings.md + title: Configuration methods + - page: docs-content://deploy-manage/deploy/self-managed/setup-configuration-memory.md + title: Disable swapping + - page: docs-content://deploy-manage/deploy/self-managed/file-descriptors.md + title: File descriptors + - page: docs-content://deploy-manage/deploy/self-managed/vm-max-map-count.md + title: Virtual memory + - page: docs-content://deploy-manage/deploy/self-managed/max-number-of-threads.md + title: Increase max number of threads + - page: docs-content://deploy-manage/deploy/self-managed/executable-jna-tmpdir.md + title: JNA temporary directory + - page: docs-content://deploy-manage/deploy/self-managed/system-config-tcpretries.md + title: TCP retransmission timeout + - page: docs-content://deploy-manage/deploy/self-managed/bootstrap-checks.md + title: Bootstrap checks + - page: docs-content://deploy-manage/deploy/self-managed/install-elasticsearch-from-archive-on-linux-macos.md + title: Linux or macOS archive + - page: docs-content://deploy-manage/deploy/self-managed/install-elasticsearch-with-zip-on-windows.md + title: Windows .zip + - page: docs-content://deploy-manage/deploy/self-managed/install-elasticsearch-with-debian-package.md + title: Debian package + - page: docs-content://deploy-manage/deploy/self-managed/install-elasticsearch-with-rpm.md + title: RPM + - group: Docker + page: docs-content://deploy-manage/deploy/self-managed/install-elasticsearch-with-docker.md + children: + - page: docs-content://deploy-manage/deploy/self-managed/install-elasticsearch-docker-basic.md + title: Single-node cluster + - page: docs-content://deploy-manage/deploy/self-managed/install-elasticsearch-docker-compose.md + title: Multi-node with Docker Compose + - page: docs-content://deploy-manage/deploy/self-managed/install-elasticsearch-docker-prod.md + title: Docker in production + - page: docs-content://deploy-manage/deploy/self-managed/install-elasticsearch-docker-configure.md + title: Configure with Docker + - page: docs-content://deploy-manage/deploy/self-managed/important-settings-configuration.md + title: Important Elasticsearch settings + - page: docs-content://deploy-manage/deploy/self-managed/configure-elasticsearch.md # BOTH PHASES + title: Configure Elasticsearch + - group: Install Kibana + page: docs-content://deploy-manage/deploy/self-managed/install-kibana.md + children: + - page: docs-content://deploy-manage/deploy/self-managed/install-kibana-from-archive-on-linux-macos.md + title: Linux or macOS archive + - page: docs-content://deploy-manage/deploy/self-managed/install-kibana-on-windows.md + title: Windows + - page: docs-content://deploy-manage/deploy/self-managed/install-kibana-with-debian-package.md + title: Debian package + - page: docs-content://deploy-manage/deploy/self-managed/install-kibana-with-rpm.md + title: RPM + - page: docs-content://deploy-manage/deploy/self-managed/install-kibana-with-docker.md + title: Docker + - page: docs-content://deploy-manage/deploy/self-managed/configure-kibana.md # BOTH PHASES + title: Configure Kibana + - title: Access Elasticsearch + - page: docs-content://deploy-manage/deploy/self-managed/access-kibana.md # BOTH PHASES + title: Access Kibana + - page: docs-content://deploy-manage/deploy/self-managed/air-gapped-install.md + title: Air-gapped install + - group: Tutorials + page: docs-content://deploy-manage/deploy/self-managed/tutorials.md + children: + - page: docs-content://deploy-manage/deploy/self-managed/tutorial-self-managed-install.md + title: Install a self-managed Elastic Stack + - page: docs-content://deploy-manage/deploy/self-managed/tutorial-self-managed-secure.md + title: Customize TLS certificates for a self-managed Elastic Stack + - title: Production readiness checklist + - group: Administer your orchestrator, deployment, or project + page: docs-content://deploy-manage/index.md + children: + - title: Administration skills + - label: Deployment-specific administration + - group: Elastic Cloud Hosted + children: + - group: Manage deployments + page: docs-content://deploy-manage/deploy/elastic-cloud/manage-deployments.md + children: + - group: Configure + page: docs-content://deploy-manage/deploy/elastic-cloud/configure.md + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/ec-change-hardware-profile.md + title: Manage hardware profiles + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/change-hardware.md + title: Change instance configuration + - page: docs-content://deploy-manage/deploy/elastic-cloud/ec-customize-deployment-components.md # BOTH PHASES + title: Customize deployment components + - page: docs-content://deploy-manage/deploy/elastic-cloud/edit-stack-settings.md + title: Edit stack settings + - page: docs-content://deploy-manage/deploy/elastic-cloud/add-plugins-extensions.md + title: Add plugins and extensions + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/upload-custom-plugins-bundles.md + title: Upload custom plugins + - page: docs-content://deploy-manage/deploy/elastic-cloud/manage-plugins-extensions-through-api.md + title: Manage through the API + - page: docs-content://deploy-manage/deploy/elastic-cloud/custom-endpoint-aliases.md + title: Custom endpoint aliases + - page: docs-content://deploy-manage/deploy/elastic-cloud/manage-integrations-server.md + title: Manage Integrations Server + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/switch-from-apm-to-integrations-server-payload.md + title: Switch from APM to Integrations Server + - page: docs-content://deploy-manage/deploy/elastic-cloud/find-cloud-id.md + title: Find your Cloud ID + - page: docs-content://deploy-manage/deploy/elastic-cloud/manage-deployments-using-elastic-cloud-api.md + title: Manage deployments using the API + - page: docs-content://deploy-manage/deploy/elastic-cloud/keep-track-of-deployment-activity.md + title: Keep track of deployment activity + - page: docs-content://deploy-manage/deploy/elastic-cloud/access-kibana.md # BOTH PHASES + title: Access Kibana + - page: docs-content://deploy-manage/deploy/elastic-cloud/ec-vcpu-boost-instance.md + title: vCPU boosting and credits + - page: docs-content://deploy-manage/deploy/elastic-cloud/available-stack-versions.md + title: Available stack versions + - page: docs-content://deploy-manage/deploy/elastic-cloud/restrictions-known-problems.md + title: Restrictions and known problems + - page: docs-content://deploy-manage/deploy/elastic-cloud/tools-apis.md + title: Tools and APIs + - group: Elastic Cloud Serverless + children: + - page: docs-content://deploy-manage/deploy/elastic-cloud/project-settings.md + title: Manage project settings + - page: docs-content://deploy-manage/deploy/elastic-cloud/manage-serverless-projects-using-api.md + title: Manage projects with API + - page: docs-content://deploy-manage/deploy/elastic-cloud/tools-apis.md + title: Tools and APIs + - group: Elastic Cloud Enterprise + children: + - group: Manage your orchestrator + page: docs-content://deploy-manage/deploy/cloud-enterprise/configure.md + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/log-into-cloud-ui.md # BOTH PHASES + title: Log into the Cloud UI + - page: docs-content://deploy-manage/deploy/cloud-enterprise/assign-roles-to-hosts.md + title: Assign roles to hosts + - page: docs-content://deploy-manage/deploy/cloud-enterprise/system-deployments-configuration.md # BOTH PHASES + title: System deployments configuration + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/default-system-deployment-versions.md + title: Default system deployment versions + - group: Deployment templates + page: docs-content://deploy-manage/deploy/cloud-enterprise/configure-deployment-templates.md # BOTH PHASES + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/deployment-templates.md # BOTH PHASES + title: Deployment templates reference + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-configuring-ece-tag-allocators.md + title: Tag allocators + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-configuring-ece-instance-configurations-edit.md + title: Edit instance configurations + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-configuring-ece-instance-configurations-create.md + title: Create instance configurations + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-configuring-ece-create-templates.md + title: Create templates + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-configuring-ece-configure-system-templates.md + title: Configure default templates + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-configure-templates-index-management.md + title: Configure index management + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ce-add-support-for-node-roles-autoscaling.md + title: Data tiers and autoscaling support + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-ce-add-support-for-integrations-server.md + title: Integrations server support + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-configuring-ece-instance-configurations-default.md + title: Default instance configurations + - page: docs-content://deploy-manage/deploy/cloud-enterprise/change-ece-api-url.md + title: Change the API URL + - page: docs-content://deploy-manage/deploy/cloud-enterprise/change-endpoint-urls.md + title: Change endpoint URLs + - page: docs-content://deploy-manage/deploy/cloud-enterprise/enable-custom-endpoint-aliases.md + title: Enable custom endpoint aliases + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-manage-capacity.md + title: Manage allocator capacity + - page: docs-content://deploy-manage/deploy/cloud-enterprise/configure-allocator-affinity.md + title: Configure allocator affinity + - page: docs-content://deploy-manage/deploy/cloud-enterprise/change-allocator-disconnect-timeout.md + title: Change allocator disconnect timeout + - page: docs-content://deploy-manage/deploy/cloud-enterprise/install-ece-on-additional-hosts.md + title: Add hosts + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/generate-roles-tokens.md + title: Manage roles tokens + - page: docs-content://deploy-manage/deploy/cloud-enterprise/migrate-ece-to-podman-hosts.md + title: Migrate to Podman + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/migrate-to-podman-5.md + title: Migrate to Podman 5 + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-include-additional-kibana-plugin.md + title: Include additional Kibana plugins + - page: docs-content://deploy-manage/deploy/cloud-enterprise/manage-elastic-stack-versions.md + title: Manage stack versions + - page: docs-content://deploy-manage/deploy/cloud-enterprise/statistics-collected-by-cloud-enterprise.md + title: Statistics collected by ECE + - page: docs-content://deploy-manage/deploy/cloud-enterprise/tools-apis.md + title: Tools and APIs + - group: Manage deployments + page: docs-content://deploy-manage/deploy/cloud-enterprise/working-with-deployments.md + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/deployment-templates.md # BOTH PHASES + title: Deployment templates reference + - page: docs-content://deploy-manage/deploy/cloud-enterprise/create-deployment.md # BOTH PHASES + title: Create a deployment + - page: docs-content://deploy-manage/deploy/cloud-enterprise/access-kibana.md # BOTH PHASES + title: Access Kibana + - page: docs-content://deploy-manage/deploy/cloud-enterprise/connect-elasticsearch.md # BOTH PHASES + title: Connect to Elasticsearch + - page: docs-content://deploy-manage/deploy/cloud-enterprise/configure-deployment.md + title: Configure + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/customize-deployment.md + title: Customize deployment components + - page: docs-content://deploy-manage/deploy/cloud-enterprise/edit-stack-settings.md + title: Edit stack settings + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/edit-stack-settings-elasticsearch.md + title: Elasticsearch user settings + - page: docs-content://deploy-manage/deploy/cloud-enterprise/edit-stack-settings-kibana.md + title: Kibana user settings + - page: docs-content://deploy-manage/deploy/cloud-enterprise/edit-stack-settings-apm.md + title: APM user settings + - page: docs-content://deploy-manage/deploy/cloud-enterprise/edit-stack-settings-enterprise.md + title: Enterprise search user settings + - page: docs-content://deploy-manage/deploy/cloud-enterprise/resize-deployment.md + title: Resize a deployment + - page: docs-content://deploy-manage/deploy/cloud-enterprise/add-plugins.md + title: Add plugins and extensions + - page: docs-content://deploy-manage/deploy/cloud-enterprise/add-custom-bundles-plugins.md + title: Add custom bundles and plugins + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-regional-deployment-aliases.md + title: Custom endpoint aliases + - page: docs-content://deploy-manage/deploy/cloud-enterprise/resource-overrides.md + title: Resource overrides + - page: docs-content://deploy-manage/deploy/cloud-enterprise/advanced-cluster-configuration.md + title: Advanced cluster configuration + - page: docs-content://deploy-manage/deploy/cloud-enterprise/search-filter-deployments.md + title: Search and filter deployments + - page: docs-content://deploy-manage/deploy/cloud-enterprise/keep-track-of-deployment-activity.md + title: Keep track of deployment activity + - page: docs-content://deploy-manage/deploy/cloud-enterprise/manage-integrations-server.md + title: Manage Integrations Server + children: + - page: docs-content://deploy-manage/deploy/cloud-enterprise/ece-integrations-server-api-example.md + title: Enable through the API + - page: docs-content://deploy-manage/deploy/cloud-enterprise/switch-from-apm-to-integrations-server-payload.md + title: Switch from APM + - group: Elastic Cloud on Kubernetes + children: + - group: Manage the ECK operator + page: docs-content://deploy-manage/deploy/cloud-on-k8s/configure.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/configure-eck.md + title: Apply configuration settings + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/configure-validating-webhook.md + title: Validating webhook + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/restrict-cross-namespace-resource-associations.md + title: Cross-namespace restrictions + - group: Service meshes + page: docs-content://deploy-manage/deploy/cloud-on-k8s/service-meshes.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-service-mesh-istio.md + title: Istio + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-service-mesh-linkerd.md + title: Linkerd + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/webhook-namespace-selectors.md + title: Webhook namespace selectors + - group: Manage deployments and workloads + page: docs-content://deploy-manage/deploy/cloud-on-k8s/manage-deployments.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/managing-deployments-using-helm-chart.md + title: Elastic Stack Helm chart + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/update-deployments.md + title: Applying updates + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/accessing-services.md # BOTH PHASES + title: Accessing services + - group: Configure deployments + page: docs-content://deploy-manage/deploy/cloud-on-k8s/configure-deployments.md + children: + - group: Elasticsearch configuration + page: docs-content://deploy-manage/deploy/cloud-on-k8s/elasticsearch-configuration.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/nodes-orchestration.md + title: Nodes orchestration + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/storage-recommendations.md + title: Storage recommendations + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/node-configuration.md + title: Node configuration + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/volume-claim-templates.md + title: Volume claim templates + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/virtual-memory.md + title: Virtual memory + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/settings-managed-by-eck.md + title: Settings managed by ECK + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/custom-configuration-files-plugins.md + title: Custom configuration files and plugins + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/init-containers-for-plugin-downloads.md + title: Init containers for plugins + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/update-strategy.md + title: Update strategy + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/pod-disruption-budget.md + title: Pod disruption budget + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/advanced-elasticsearch-node-scheduling.md + title: Advanced node scheduling + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/readiness-probe.md + title: Readiness probe + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/pod-prestop-hook.md + title: Pod PreStop hook + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/security-context.md + title: Security context + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/requests-routing-to-elasticsearch-nodes.md + title: Traffic splitting + - group: Kibana configuration + page: docs-content://deploy-manage/deploy/cloud-on-k8s/kibana-configuration.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-kibana-es.md + title: Connect Kibana to Elasticsearch + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-kibana-advanced-configuration.md + title: Advanced configuration + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-kibana-plugins.md + title: Kibana plugins + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/customize-pods.md + title: Customize pods + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/propagate-labels-annotations.md + title: Labels and annotations + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/manage-compute-resources.md + title: Compute resources + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/recipes.md + title: Recipes + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/connect-to-external-elastic-resources.md + title: Connect to external resources + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/elastic-stack-configuration-policies.md + title: Stack configuration policies + - group: Other Elastic applications + page: docs-content://deploy-manage/deploy/cloud-on-k8s/orchestrate-other-elastic-applications.md + children: + - group: APM Server + page: docs-content://deploy-manage/deploy/cloud-on-k8s/apm-server.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/use-an-elasticsearch-cluster-managed-by-eck.md + title: Use an ECK-managed cluster + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/advanced-configuration.md + title: Advanced configuration + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/connect-to-apm-server.md + title: Connect to APM Server + - group: Standalone Elastic Agent + page: docs-content://deploy-manage/deploy/cloud-on-k8s/standalone-elastic-agent.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/quickstart-standalone.md + title: Quickstart + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/configuration-standalone.md + title: Configuration + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/configuration-examples-standalone.md + title: Configuration examples + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-openshift-agent.md + title: Agent on OpenShift + - group: Fleet-managed Elastic Agent + page: docs-content://deploy-manage/deploy/cloud-on-k8s/fleet-managed-elastic-agent.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/quickstart-fleet.md + title: Quickstart + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/configuration-fleet.md + title: Configuration + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/configuration-examples-fleet.md + title: Configuration examples + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/known-limitations.md + title: Known limitations + - group: Elastic Maps Server + page: docs-content://deploy-manage/deploy/cloud-on-k8s/elastic-maps-server.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/deploy-elastic-maps-server.md + title: Deploy Maps Server + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/map-data.md + title: Map data + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/advanced-configuration-maps-server.md + title: Advanced configuration + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/http-configuration.md + title: HTTP configuration + - group: Beats + page: docs-content://deploy-manage/deploy/cloud-on-k8s/beats.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/quickstart-beats.md + title: Quickstart + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/configuration-beats.md + title: Configuration + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/configuration-examples-beats.md + title: Configuration examples + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/troubleshooting-beats.md + title: Troubleshooting + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/k8s-openshift-beats.md + title: Beats on OpenShift + - group: Logstash + page: docs-content://deploy-manage/deploy/cloud-on-k8s/logstash.md + children: + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/quickstart-logstash.md + title: Quickstart + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/configuration-logstash.md + title: Configuration + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/securing-logstash-api.md + title: Securing the API + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/logstash-plugins.md + title: Plugins + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/configuration-examples-logstash.md + title: Configuration examples + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/update-strategy-logstash.md + title: Update strategy + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/advanced-configuration-logstash.md + title: Advanced configuration + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/package-registry.md + title: Elastic Package Registry + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/create-custom-images.md + title: Custom images + - page: docs-content://deploy-manage/deploy/cloud-on-k8s/tools-apis.md + title: Tools and APIs + - group: Self-managed clusters + children: + - page: docs-content://deploy-manage/deploy/self-managed/configure-elasticsearch.md # BOTH PHASES + title: Configure Elasticsearch + - page: docs-content://deploy-manage/deploy/self-managed/configure-kibana.md + title: Configure Kibana + - page: docs-content://deploy-manage/deploy/self-managed/plugins.md + title: Manage plugins + - page: docs-content://deploy-manage/deploy/self-managed/access-kibana.md + title: Access Kibana + - page: docs-content://deploy-manage/deploy/self-managed/tools-apis.md + title: Tools and APIs + - label: ─── + - group: Security and encryption + page: docs-content://deploy-manage/security.md + children: + - page: docs-content://deploy-manage/security/secure-hosting-environment.md + title: "Secure your orchestrator" + children: + - page: docs-content://deploy-manage/security/secure-your-elastic-cloud-enterprise-installation.md + title: "Elastic Cloud Enterprise" + children: + - page: docs-content://deploy-manage/security/secure-your-elastic-cloud-enterprise-installation/manage-security-certificates.md + title: "Manage security certificates" + - page: docs-content://deploy-manage/security/secure-your-elastic-cloud-enterprise-installation/allow-x509-certificates-signed-with-sha-1.md + title: "Allow X.509 certificates signed with SHA-1" + - page: docs-content://deploy-manage/security/secure-your-elastic-cloud-enterprise-installation/configure-tls-version.md + title: "Configure the TLS version" + - page: docs-content://deploy-manage/security/secure-your-elastic-cloud-enterprise-installation/migrate-ece-on-podman-hosts-to-selinux-enforce.md + title: "Migrate ECE on Podman hosts to SELinux enforce" + - page: docs-content://deploy-manage/security/secure-your-eck-installation.md + title: "Elastic Cloud on Kubernetes" + - page: docs-content://deploy-manage/security/secure-your-cluster-deployment.md + title: "Secure your cluster, deployment, or project" + children: + - page: docs-content://deploy-manage/security/self-setup.md + title: "Self-managed security setup" + children: + - page: docs-content://deploy-manage/security/self-auto-setup.md + title: "Automatic security setup" + - page: docs-content://deploy-manage/security/set-up-minimal-security.md + title: "Minimal security setup" + - page: docs-content://deploy-manage/security/set-up-basic-security.md + title: "Set up transport TLS" + - page: docs-content://deploy-manage/security/set-up-basic-security-plus-https.md + title: "Set up HTTPS" + - page: docs-content://deploy-manage/security/using-kibana-with-security.md + title: "Configure security in Kibana" + - page: docs-content://deploy-manage/security/secure-cluster-communications.md + title: "Manage TLS encryption" + children: + - page: docs-content://deploy-manage/security/self-tls.md + title: "Self-managed" + children: + - page: docs-content://deploy-manage/security/updating-certificates.md + title: "Update TLS certificates" + children: + - page: docs-content://deploy-manage/security/same-ca.md + title: "With the same CA" + - page: docs-content://deploy-manage/security/different-ca.md + title: "With a different CA" + - page: docs-content://deploy-manage/security/kibana-es-mutual-tls.md + title: "Mutual authentication" + - page: docs-content://deploy-manage/security/supported-ssltls-versions-by-jdk-version.md + title: "Supported SSL/TLS versions by JDK version" + - page: docs-content://deploy-manage/security/enabling-cipher-suites-for-stronger-encryption.md + title: "Enabling cipher suites for stronger encryption" + - page: docs-content://deploy-manage/security/eck-tls.md + title: "ECK" + children: + - page: docs-content://deploy-manage/security/k8s-https-settings.md + title: "Manage HTTP certificates on ECK" + - page: docs-content://deploy-manage/security/k8s-transport-settings.md + title: Manage transport certificates on ECK + - page: docs-content://deploy-manage/security/k8s-es-client-certificate-auth.md + title: "Client certificate authentication" + - page: docs-content://deploy-manage/security/external-ca-transport.md + title: "External CA for TLS" + - page: docs-content://deploy-manage/security/network-security.md + title: "Network security" + children: + - page: docs-content://deploy-manage/security/network-security-policies.md + title: "How network security policies work in Cloud" + - page: docs-content://deploy-manage/security/ece-filter-rules.md + title: "How IP filtering rules work in ECE" + - page: docs-content://deploy-manage/security/ip-filtering.md + title: "Add IP filters" + children: + - page: docs-content://deploy-manage/security/ip-filtering-cloud.md + title: "In ECH or Serverless" + - page: docs-content://deploy-manage/security/ip-filtering-ece.md + title: "In ECE" + - page: docs-content://deploy-manage/security/ip-filtering-basic.md + title: "In ECK and Self Managed" + - page: docs-content://deploy-manage/security/remote-cluster-filtering.md + title: "Remote cluster filters" + - page: docs-content://deploy-manage/security/private-connectivity.md + title: "Private connectivity" + children: + - page: docs-content://deploy-manage/security/private-connectivity-aws.md + title: "AWS PrivateLink" + - page: docs-content://deploy-manage/security/private-connectivity-azure.md + title: "Azure Private Link" + - page: docs-content://deploy-manage/security/private-connectivity-gcp.md + title: "GCP Private Service Connect" + - page: docs-content://deploy-manage/security/claim-private-connection-api.md + title: "Claim private connection ownership" + - page: docs-content://deploy-manage/security/network-security-api.md + title: "Through the API" + - page: docs-content://deploy-manage/security/k8s-network-policies.md + title: "Kubernetes network policies" + - page: docs-content://deploy-manage/security/elastic-cloud-static-ips.md + title: "Elastic Cloud static IPs" + - page: docs-content://deploy-manage/security/kibana-session-management.md + title: "Kibana session management" + - page: docs-content://deploy-manage/security/data-security.md + title: "Encrypt your deployment data" + children: + - page: docs-content://deploy-manage/security/encrypt-deployment-with-customer-managed-encryption-key.md + title: "Use a customer-managed encryption key" + - page: docs-content://deploy-manage/security/secure-settings.md + title: "Secure your settings" + children: + - page: docs-content://deploy-manage/security/k8s-secure-settings.md + title: "Secure settings on ECK" + - page: docs-content://deploy-manage/security/secure-saved-objects.md + title: "Secure Kibana saved objects" + - page: docs-content://deploy-manage/security/logging-configuration/security-event-audit-logging.md + title: "Security event audit logging" + children: + - page: docs-content://deploy-manage/security/logging-configuration/enabling-audit-logs.md + title: "Enable audit logging" + - page: docs-content://deploy-manage/security/logging-configuration/configuring-audit-logs.md + title: Configure audit logging + children: + - page: docs-content://deploy-manage/security/logging-configuration/logfile-audit-events-ignore-policies.md + title: "Elasticsearch audit events ignore policies" + - page: docs-content://deploy-manage/security/logging-configuration/logfile-audit-output.md + title: "Elasticsearch logfile output" + - page: docs-content://deploy-manage/security/logging-configuration/auditing-search-queries.md + title: Audit Elasticsearch search queries + - page: docs-content://deploy-manage/security/logging-configuration/correlating-kibana-elasticsearch-audit-logs.md + title: "Correlate audit events" + - page: docs-content://deploy-manage/security/secure-clients-integrations.md + title: Secure other Elastic Stack components + - page: docs-content://deploy-manage/security/httprest-clients-security.md + title: Securing HTTP client applications + - page: docs-content://deploy-manage/security/limitations.md + title: "Limitations" + - page: docs-content://deploy-manage/security/fips.md + title: FIPS compliance + children: + - page: docs-content://deploy-manage/security/fips-es.md + title: FIPS compliance for Elasticsearch + - page: docs-content://deploy-manage/security/fips-kib.md + title: FIPS compliance for Kibana + - page: docs-content://deploy-manage/security/fips-ingest.md + title: FIPS mode for Ingest tools + - group: Authentication and authorization + page: docs-content://deploy-manage/users-roles.md + children: + - page: docs-content://deploy-manage/users-roles/cloud-organization.md + title: "Cloud organization" + children: + - page: docs-content://deploy-manage/users-roles/cloud-organization/manage-users.md + title: Manage users + - page: docs-content://deploy-manage/users-roles/cloud-organization/user-roles.md + title: User roles and privileges + - page: docs-content://deploy-manage/users-roles/cloud-organization/configure-saml-authentication.md + title: "Configure SAML SSO" + children: + - page: docs-content://deploy-manage/users-roles/cloud-organization/register-elastic-cloud-saml-in-okta.md + title: "Okta" + - page: docs-content://deploy-manage/users-roles/cloud-organization/register-elastic-cloud-saml-in-microsoft-entra-id.md + title: "Microsoft Entra ID" + - page: docs-content://deploy-manage/users-roles/cloud-enterprise-orchestrator.md + title: "ECE orchestrator" + children: + - page: docs-content://deploy-manage/users-roles/cloud-enterprise-orchestrator/manage-system-passwords.md + title: Manage system passwords + - page: docs-content://deploy-manage/users-roles/cloud-enterprise-orchestrator/manage-users-roles.md + title: Manage users and roles + children: + - page: docs-content://deploy-manage/users-roles/cloud-enterprise-orchestrator/native-user-authentication.md + title: Native users + - page: docs-content://deploy-manage/users-roles/cloud-enterprise-orchestrator/active-directory.md + title: Active Directory + - page: docs-content://deploy-manage/users-roles/cloud-enterprise-orchestrator/ldap.md + title: LDAP + - page: docs-content://deploy-manage/users-roles/cloud-enterprise-orchestrator/saml.md + title: SAML + - page: docs-content://deploy-manage/users-roles/cloud-enterprise-orchestrator/configure-sso-for-deployments.md + title: Configure SSO for deployments + - page: docs-content://deploy-manage/users-roles/serverless-custom-roles.md + title: Serverless project custom roles + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth.md + title: "Cluster or deployment" + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/quickstart.md + title: "Quickstart" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/user-authentication.md + title: User authentication + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/authentication-realms.md + title: Authentication realms + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/realm-chains.md + title: Realm chains + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/security-domains.md + title: Security domains + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/internal-authentication.md + title: Internal authentication + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/native.md + title: "Native" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/file-based.md + title: "File-based" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/external-authentication.md + title: External authentication + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/active-directory.md + title: "Active Directory" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/jwt.md + title: "JWT" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/kerberos.md + title: "Kerberos" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/ldap.md + title: "LDAP" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/openid-connect.md + title: "OpenID Connect" + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/oidc-examples.md + title: "With Azure, Google, or Okta" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/saml.md + title: "SAML" + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/saml-entra.md + title: "With Microsoft Entra ID" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/pki.md + title: PKI + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/custom.md + title: Custom realms + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/built-in-users.md + title: "Built-in users" + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/built-in-sm.md + title: "Change passwords" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/orchestrator-managed-users-overview.md + title: "Orchestrator-managed users" + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/manage-elastic-user-cloud.md + title: "ECH and ECE" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/managed-credentials-eck.md + title: "ECK managed credentials" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/kibana-authentication.md + title: "Kibana authentication" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/access-agreement.md + title: Kibana access agreement + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/anonymous-access.md + title: Anonymous access + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/token-based-authentication-services.md + title: Token-based authentication services + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/service-accounts.md + title: Service accounts + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/internal-users.md + title: Internal users + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/operator-privileges.md + title: Operator privileges + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/configure-operator-privileges.md + title: Configure operator privileges + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/operator-only-functionality.md + title: Operator-only functionality + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/operator-privileges-for-snapshot-restore.md + title: Operator privileges for snapshot and restore + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/user-profiles.md + title: User profiles + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/looking-up-users-without-authentication.md + title: Looking up users without authentication + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/controlling-user-cache.md + title: Controlling the user cache + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/manage-authentication-for-multiple-clusters.md + title: Manage authentication for multiple clusters + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/user-roles.md + title: User roles + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/defining-roles.md + title: Defining roles + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/role-structure.md + title: Role structure + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/granting-privileges-for-data-streams-aliases.md + title: "For data streams and aliases" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/kibana-role-management.md + title: "Using Kibana" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/role-restriction.md + title: Role restriction + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/kibana-privileges.md + title: Kibana privileges + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/mapping-users-groups-to-roles.md + title: "Map users and groups to roles" + children: + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/role-mapping-resources.md + title: "Role mapping properties" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/authorization-delegation.md + title: Authorization delegation + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/authorization-plugins.md + title: Authorization plugins + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/controlling-access-at-document-field-level.md + title: "Control access at the document and field level" + - page: docs-content://deploy-manage/users-roles/cluster-or-deployment-auth/submitting-requests-on-behalf-of-other-users.md + title: "Submit requests on behalf of other users" + - group: API keys + page: docs-content://deploy-manage/api-keys.md + children: + - page: docs-content://deploy-manage/api-keys/elasticsearch-api-keys.md + title: Elasticsearch API keys + - page: docs-content://deploy-manage/api-keys/serverless-project-api-keys.md + title: Serverless project API keys + - page: docs-content://deploy-manage/api-keys/elastic-cloud-api-keys.md + title: Elastic Cloud API keys + - page: docs-content://deploy-manage/api-keys/elastic-cloud-enterprise-api-keys.md + title: Elastic Cloud Enterprise API keys + - group: Spaces + page: docs-content://deploy-manage/manage-spaces.md + - group: Monitoring + page: docs-content://deploy-manage/monitor.md + children: + - page: docs-content://deploy-manage/monitor/autoops.md + title: AutoOps + children: + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-how-to-access.md + title: "For Elastic Cloud Hosted" + - page: docs-content://deploy-manage/monitor/autoops/autoops-for-serverless.md + title: "For Elastic Cloud Serverless" + children: + - page: docs-content://deploy-manage/monitor/autoops/access-autoops-for-serverless.md + title: "Access AutoOps in your project" + - page: docs-content://deploy-manage/monitor/autoops/search-tier-view-autoops-serverless.md + title: "Search Tier view" + - page: docs-content://deploy-manage/monitor/autoops/indexing-tier-view-autoops-serverless.md + title: "Indexing Tier view" + - page: docs-content://deploy-manage/monitor/autoops/search-ai-lake-view-autoops-serverless.md + title: "Search AI Lake view" + - page: docs-content://deploy-manage/monitor/autoops/cc-autoops-as-cloud-connected.md + title: "For ECE, ECK, and self-managed clusters" + children: + - page: docs-content://deploy-manage/monitor/autoops/cc-connect-self-managed-to-autoops.md + title: "Connect your cluster" + - page: docs-content://deploy-manage/monitor/autoops/cc-connect-local-dev-to-autoops.md + title: "Connect your local development cluster" + - page: docs-content://deploy-manage/monitor/autoops/autoops-sm-custom-certification.md + title: "Configure Elastic agent with custom certificate" + - page: docs-content://deploy-manage/monitor/autoops/autoops-disable-metrics-collection.md + title: "Disable certain types of data collection" + - page: docs-content://deploy-manage/monitor/autoops/cc-manage-users.md + title: "Manage connected cluster users" + - page: docs-content://deploy-manage/monitor/autoops/cc-cloud-connect-autoops-troubleshooting.md + title: "Troubleshooting" + children: + - page: docs-content://deploy-manage/monitor/autoops/autoops-connectivity-check.md + title: "Run the Connectivity Check" + - page: docs-content://deploy-manage/monitor/autoops/autoops-sm-troubleshoot-firewalls.md + title: "Firewalls blocking Elastic Agent" + - page: docs-content://deploy-manage/monitor/autoops/autoops-sm-troubleshoot-eck-no-clusters.md + title: "Connected clusters not appearing with ECK" + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-regions.md + title: "Regions" + - page: docs-content://deploy-manage/monitor/autoops/views.md + title: "Views" + children: + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-overview-view.md + title: "Overview" + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-deployment-view.md + title: "Deployment or Cluster" + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-nodes-view.md + title: "Nodes" + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-index-view.md + title: "Indices" + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-shards-view.md + title: "Shards" + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-template-optimizer.md + title: "Template Optimizer" + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-events.md + title: "Events" + children: + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-event-settings.md + title: Event Settings + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-notifications-settings.md + title: Notifications Settings + - page: docs-content://deploy-manage/monitor/autoops/ec-autoops-faq.md + title: "FAQ" + - page: docs-content://deploy-manage/monitor/stack-monitoring.md + title: Stack monitoring + children: + - page: docs-content://deploy-manage/monitor/stack-monitoring/ece-ech-stack-monitoring.md + title: "Enable on ECH and ECE" + - page: docs-content://deploy-manage/monitor/stack-monitoring/eck-stack-monitoring.md + title: "Enable on ECK" + - page: docs-content://deploy-manage/monitor/stack-monitoring/elasticsearch-monitoring-self-managed.md + title: "Self-managed: Elasticsearch" + children: + - page: docs-content://deploy-manage/monitor/stack-monitoring/collecting-monitoring-data-with-elastic-agent.md + title: "Collecting monitoring data with Elastic Agent" + - page: docs-content://deploy-manage/monitor/stack-monitoring/collecting-monitoring-data-with-metricbeat.md + title: "Collecting monitoring data with Metricbeat" + - page: docs-content://deploy-manage/monitor/stack-monitoring/collecting-log-data-with-filebeat.md + title: "Collecting log data with Filebeat" + - page: docs-content://deploy-manage/monitor/stack-monitoring/es-self-monitoring-prod.md + title: Monitoring in a production environment + - page: docs-content://deploy-manage/monitor/stack-monitoring/es-legacy-collection-methods.md + title: "Legacy collection methods" + children: + - page: docs-content://deploy-manage/monitor/stack-monitoring/es-monitoring-collectors.md + title: Collectors + - page: docs-content://deploy-manage/monitor/stack-monitoring/es-monitoring-exporters.md + title: Exporters + - page: docs-content://deploy-manage/monitor/stack-monitoring/es-local-exporter.md + title: Local exporters + - page: docs-content://deploy-manage/monitor/stack-monitoring/es-http-exporter.md + title: HTTP exporters + - page: docs-content://deploy-manage/monitor/stack-monitoring/es-pause-export.md + title: Pausing data collection + - page: docs-content://deploy-manage/monitor/stack-monitoring/kibana-monitoring-self-managed.md + title: "Self-managed: Kibana" + children: + - page: docs-content://deploy-manage/monitor/stack-monitoring/kibana-monitoring-elastic-agent.md + title: "Collect monitoring data with Elastic Agent" + - page: docs-content://deploy-manage/monitor/stack-monitoring/kibana-monitoring-metricbeat.md + title: "Collect monitoring data with Metricbeat" + - page: docs-content://deploy-manage/monitor/stack-monitoring/kibana-monitoring-legacy.md + title: "Legacy collection methods" + - page: docs-content://deploy-manage/monitor/stack-monitoring/kibana-monitoring-data.md + title: Access monitoring data in Kibana + - page: docs-content://deploy-manage/monitor/monitoring-data/visualizing-monitoring-data.md + title: Visualizing monitoring data + children: + - page: docs-content://deploy-manage/monitor/monitoring-data/beats-page.md + title: Beats metrics + - page: docs-content://deploy-manage/monitor/monitoring-data/elasticsearch-metrics.md + title: Elasticsearch metrics + - page: docs-content://deploy-manage/monitor/monitoring-data/kibana-page.md + title: Kibana metrics + - page: docs-content://deploy-manage/monitor/monitoring-data/integrations-server-page.md + title: Integrations Server metrics + - page: docs-content://deploy-manage/monitor/monitoring-data/logstash-page.md + title: Logstash metrics + - page: docs-content://deploy-manage/monitor/monitoring-data/monitor-troubleshooting.md + title: "Troubleshooting" + - page: docs-content://deploy-manage/monitor/monitoring-data/configure-stack-monitoring-alerts.md + title: Stack monitoring alerts + - page: docs-content://deploy-manage/monitor/monitoring-data/configuring-data-streamsindices-for-monitoring.md + title: Configuring monitoring data streams and indices + children: + - page: docs-content://deploy-manage/monitor/monitoring-data/config-monitoring-data-streams-elastic-agent.md + title: Configuring data streams created by Elastic Agent + - page: docs-content://deploy-manage/monitor/monitoring-data/config-monitoring-data-streams-metricbeat-8.md + title: Configuring data streams created by Metricbeat 8 + - page: docs-content://deploy-manage/monitor/monitoring-data/config-monitoring-indices-metricbeat-7-internal-collection.md + title: Configuring indices created by Metricbeat 7 or internal collection + - page: docs-content://deploy-manage/monitor/autoops-vs-stack-monitoring.md + title: "AutoOps vs. Stack Monitoring" + - page: docs-content://deploy-manage/monitor/cloud-health-perf.md + title: "Cloud deployment health" + children: + - page: docs-content://deploy-manage/monitor/access-performance-metrics-on-elastic-cloud.md + title: Performance metrics on Elastic Cloud + - page: docs-content://deploy-manage/monitor/ec-memory-pressure.md + title: JVM memory pressure indicator + - page: docs-content://deploy-manage/monitor/kibana-task-manager-health-monitoring.md + title: "Kibana task manager monitoring" + - page: docs-content://deploy-manage/monitor/orchestrators.md + title: Monitoring orchestrators + children: + - page: docs-content://deploy-manage/monitor/orchestrators/eck-metrics-configuration.md + title: ECK operator metrics + children: + - page: docs-content://deploy-manage/monitor/orchestrators/k8s-enabling-metrics-endpoint.md + title: Enabling the metrics endpoint + - page: docs-content://deploy-manage/monitor/orchestrators/k8s-securing-metrics-endpoint.md + title: Securing the metrics endpoint + - page: docs-content://deploy-manage/monitor/orchestrators/k8s-prometheus-requirements.md + title: Prometheus requirements + - page: docs-content://deploy-manage/monitor/orchestrators/ece-platform-monitoring.md + title: ECE platform monitoring + children: + - page: docs-content://deploy-manage/monitor/orchestrators/ece-monitoring-ece-access.md + title: Platform monitoring deployment logs and metrics + - page: docs-content://deploy-manage/monitor/orchestrators/ece-proxy-log-fields.md + title: Proxy log fields + - page: docs-content://deploy-manage/monitor/orchestrators/ece-monitoring-ece-set-retention.md + title: Set the retention period for logging and metrics indices + - page: docs-content://deploy-manage/monitor/logging-configuration.md + title: Logging + children: + - page: docs-content://deploy-manage/monitor/logging-configuration/elasticsearch-log4j-configuration-self-managed.md + title: Elasticsearch log4j configuration + - page: docs-content://deploy-manage/monitor/logging-configuration/update-elasticsearch-logging-levels.md + title: Update Elasticsearch logging levels + - page: docs-content://deploy-manage/monitor/logging-configuration/elasticsearch-deprecation-logs.md + title: Elasticsearch deprecation logs + - page: docs-content://deploy-manage/monitor/logging-configuration/slow-logs.md + title: Slow query and index logging + - page: docs-content://deploy-manage/monitor/logging-configuration/kibana-logging.md + title: Kibana logging + children: + - page: docs-content://deploy-manage/monitor/logging-configuration/kibana-log-levels.md + title: Set global log levels for Kibana + - page: docs-content://deploy-manage/monitor/logging-configuration/kib-advanced-logging.md + title: Advanced Kibana logging settings + children: + - page: docs-content://deploy-manage/monitor/logging-configuration/kibana-log-settings-examples.md + title: "Examples" + - page: docs-content://deploy-manage/kibana-reporting-configuration.md + title: Configure Kibana reporting + - group: Backup, high availability, and resilience tools + page: docs-content://deploy-manage/tools.md + children: + - page: docs-content://deploy-manage/tools/snapshot-and-restore.md + title: Snapshot and restore + children: + - page: docs-content://deploy-manage/tools/snapshot-and-restore/manage-snapshot-repositories.md + title: Manage snapshot repositories + children: + - page: docs-content://deploy-manage/tools/snapshot-and-restore/self-managed.md + title: "Self-managed" + children: + - page: docs-content://deploy-manage/tools/snapshot-and-restore/azure-repository.md + title: Azure repository + - page: docs-content://deploy-manage/tools/snapshot-and-restore/google-cloud-storage-repository.md + title: Google Cloud Storage repository + - page: docs-content://deploy-manage/tools/snapshot-and-restore/s3-repository.md + title: S3 repository + - page: docs-content://deploy-manage/tools/snapshot-and-restore/shared-file-system-repository.md + title: Shared file system repository + - page: docs-content://deploy-manage/tools/snapshot-and-restore/read-only-url-repository.md + title: Read-only URL repository + - page: docs-content://deploy-manage/tools/snapshot-and-restore/source-only-repository.md + title: Source-only repository + - page: docs-content://deploy-manage/tools/snapshot-and-restore/elastic-cloud-hosted.md + title: "Elastic Cloud Hosted" + children: + - page: docs-content://deploy-manage/tools/snapshot-and-restore/ec-aws-custom-repository.md + title: "AWS S3" + - page: docs-content://deploy-manage/tools/snapshot-and-restore/ec-gcs-snapshotting.md + title: "Google Cloud Storage" + - page: docs-content://deploy-manage/tools/snapshot-and-restore/ec-azure-snapshotting.md + title: "Azure Blob Storage" + - page: docs-content://deploy-manage/tools/snapshot-and-restore/access-isolation-for-found-snapshots-repository.md + title: Access isolation for the found-snapshots repository + children: + - page: docs-content://deploy-manage/tools/snapshot-and-restore/repository-isolation-on-azure.md + title: "Azure" + - page: docs-content://deploy-manage/tools/snapshot-and-restore/repository-isolation-on-aws-gcp.md + title: "AWS and GCP" + - page: docs-content://deploy-manage/tools/snapshot-and-restore/cloud-enterprise.md + title: "Elastic Cloud Enterprise" + children: + - page: docs-content://deploy-manage/tools/snapshot-and-restore/ece-aws-custom-repository.md + title: "AWS S3" + - page: docs-content://deploy-manage/tools/snapshot-and-restore/google-cloud-storage-gcs-repository.md + title: "Google Cloud Storage" + - page: docs-content://deploy-manage/tools/snapshot-and-restore/azure-storage-repository.md + title: Azure Storage repository + - page: docs-content://deploy-manage/tools/snapshot-and-restore/minio-on-premise-repository.md + title: MinIO self-managed repository + - page: docs-content://deploy-manage/tools/snapshot-and-restore/cloud-on-k8s.md + title: "Elastic Cloud on Kubernetes" + - page: docs-content://deploy-manage/tools/snapshot-and-restore/create-snapshots.md + title: Create, monitor and delete snapshots + - page: docs-content://deploy-manage/tools/snapshot-and-restore/restore-snapshot.md + title: Restore a snapshot + children: + - page: docs-content://deploy-manage/tools/snapshot-and-restore/ece-restore-across-clusters.md + title: Restore a snapshot across clusters + children: + - page: docs-content://deploy-manage/tools/snapshot-and-restore/ece-restore-snapshots-into-new-deployment.md + title: Restore snapshot into a new deployment + - page: docs-content://deploy-manage/tools/snapshot-and-restore/ece-restore-snapshots-into-existing-deployment.md + title: Restore snapshot into an existing deployment + - page: docs-content://deploy-manage/tools/snapshot-and-restore/ece-restore-snapshots-containing-searchable-snapshots-indices-across-clusters.md + title: Restore snapshots containing searchable snapshots indices across clusters + - page: docs-content://deploy-manage/tools/snapshot-and-restore/searchable-snapshots.md + title: Searchable snapshots + - page: docs-content://deploy-manage/tools/cross-cluster-replication.md + title: Cross-cluster replication + children: + - page: docs-content://deploy-manage/tools/cross-cluster-replication/set-up-cross-cluster-replication.md + title: "Set up cross-cluster replication" + children: + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-getting-started-prerequisites.md + title: "Prerequisites" + - page: docs-content://deploy-manage/tools/cross-cluster-replication/_connect_to_a_remote_cluster.md + title: Connect to a remote cluster + - page: docs-content://deploy-manage/tools/cross-cluster-replication/_configure_privileges_for_cross_cluster_replication_2.md + title: Configure privileges for cross-cluster replication + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-getting-started-follower-index.md + title: Create a follower index to replicate a specific index + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-getting-started-auto-follow.md + title: Create an auto-follow pattern to replicate time series indices + - page: docs-content://deploy-manage/tools/cross-cluster-replication/manage-cross-cluster-replication.md + title: Manage cross-cluster replication + children: + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-inspect-progress.md + title: Inspect replication statistics + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-pause-replication.md + title: Pause and resume replication + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-recreate-follower-index.md + title: Recreate a follower index + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-terminate-replication.md + title: Terminate replication + - page: docs-content://deploy-manage/tools/cross-cluster-replication/manage-auto-follow-patterns.md + title: Manage auto-follow patterns + children: + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-auto-follow-create.md + title: Create auto-follow patterns + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-auto-follow-retrieve.md + title: Retrieve auto-follow patterns + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-auto-follow-pause.md + title: Pause and resume auto-follow patterns + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-auto-follow-delete.md + title: Delete auto-follow patterns + - page: docs-content://deploy-manage/tools/cross-cluster-replication/upgrading-clusters.md + title: "Upgrading clusters" + children: + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-uni-directional-upgrade.md + title: Uni-directional index following + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-bi-directional-upgrade.md + title: Bi-directional index following + - page: docs-content://deploy-manage/tools/cross-cluster-replication/uni-directional-disaster-recovery.md + title: "Uni-directional disaster recovery" + children: + - page: docs-content://deploy-manage/tools/cross-cluster-replication/_prerequisites_14.md + title: "Prerequisites" + - page: docs-content://deploy-manage/tools/cross-cluster-replication/_failover_when_clustera_is_down.md + title: "Failover when clusterA is down" + - page: docs-content://deploy-manage/tools/cross-cluster-replication/_failback_when_clustera_comes_back.md + title: "Failback when clusterA comes back" + - page: docs-content://deploy-manage/tools/cross-cluster-replication/bi-directional-disaster-recovery.md + title: "Bi-directional disaster recovery" + children: + - page: docs-content://deploy-manage/tools/cross-cluster-replication/ccr-tutorial-initial-setup.md + title: Initial setup + - page: docs-content://deploy-manage/tools/cross-cluster-replication/_failover_when_clustera_is_down_2.md + title: "Failover when clusterA is down" + - page: docs-content://deploy-manage/tools/cross-cluster-replication/_failback_when_clustera_comes_back_2.md + title: "Failback when clusterA comes back" + - page: docs-content://deploy-manage/tools/cross-cluster-replication/_perform_update_or_delete_by_query.md + title: Perform update or delete by query + - group: Autoscaling + page: docs-content://deploy-manage/autoscaling.md + children: + - page: docs-content://deploy-manage/autoscaling/autoscaling-in-ece-and-ech.md + title: "In ECE and ECH" + - page: docs-content://deploy-manage/autoscaling/autoscaling-in-eck.md + title: "In ECK" + - page: docs-content://deploy-manage/autoscaling/autoscaling-deciders.md + title: Autoscaling deciders + - page: docs-content://deploy-manage/autoscaling/trained-model-autoscaling.md + title: Trained model autoscaling + - page: docs-content://deploy-manage/stack-settings.md + title: Stack settings + - page: docs-content://deploy-manage/manage-connectors.md + title: Connectors + - group: Remote clusters + page: docs-content://deploy-manage/remote-clusters.md + children: + - page: docs-content://deploy-manage/remote-clusters/security-models.md + title: "Security models" + - page: docs-content://deploy-manage/remote-clusters/connection-modes.md + title: "Connection modes" + - page: docs-content://deploy-manage/remote-clusters/ec-enable-ccs.md + title: "On Elastic Cloud Hosted" + children: + - page: docs-content://deploy-manage/remote-clusters/ec-remote-cluster-same-ess.md + title: "To the same Elastic Cloud organization" + - page: docs-content://deploy-manage/remote-clusters/ec-remote-cluster-other-ess.md + title: "To a different Elastic Cloud organization" + - page: docs-content://deploy-manage/remote-clusters/ec-remote-cluster-ece.md + title: "To Elastic Cloud Enterprise" + - page: docs-content://deploy-manage/remote-clusters/ec-remote-cluster-self-managed.md + title: "To a self-managed cluster" + - page: docs-content://deploy-manage/remote-clusters/ec-enable-ccs-for-eck.md + title: "To Elastic Cloud on Kubernetes" + - page: docs-content://deploy-manage/remote-clusters/ec-remote-cluster-strong-identity.md + title: "Strong identity verification" + - page: docs-content://deploy-manage/remote-clusters/ec-edit-remove-trusted-environment.md + title: "Manage trusted environments" + - page: docs-content://deploy-manage/remote-clusters/ec-migrate-ccs.md + title: "Migrate from the CCS deployment template" + - page: docs-content://deploy-manage/remote-clusters/ece-enable-ccs.md + title: "On Elastic Cloud Enterprise" + children: + - page: docs-content://deploy-manage/remote-clusters/ece-remote-cluster-same-ece.md + title: "To the same ECE environment" + - page: docs-content://deploy-manage/remote-clusters/ece-remote-cluster-other-ece.md + title: "To a different ECE environment" + - page: docs-content://deploy-manage/remote-clusters/ece-remote-cluster-ece-ess.md + title: "To Elastic Cloud" + - page: docs-content://deploy-manage/remote-clusters/ece-remote-cluster-self-managed.md + title: "To a self-managed cluster" + - page: docs-content://deploy-manage/remote-clusters/ece-enable-ccs-for-eck.md + title: "To Elastic Cloud on Kubernetes" + - page: docs-content://deploy-manage/remote-clusters/ece-edit-remove-trusted-environment.md + title: "Manage trusted environments" + - page: docs-content://deploy-manage/remote-clusters/ece-migrate-ccs.md + title: "Migrate from the CCS deployment template" + - page: docs-content://deploy-manage/remote-clusters/remote-clusters-self-managed.md + title: "On self-managed Elastic Stack" + children: + - page: docs-content://deploy-manage/remote-clusters/remote-clusters-api-key.md + title: Add remote clusters using API key authentication + - page: docs-content://deploy-manage/remote-clusters/remote-clusters-cert.md + title: Add remote clusters using TLS certificate authentication + - page: docs-content://deploy-manage/remote-clusters/self-remote-cluster-eck.md + title: "To Elastic Cloud on Kubernetes" + - page: docs-content://deploy-manage/remote-clusters/remote-clusters-migrate.md + title: "Migrate from certificate to API key authentication" + - page: docs-content://deploy-manage/remote-clusters/eck-remote-clusters-landing.md + title: "On Elastic Cloud on Kubernetes" + children: + - page: docs-content://deploy-manage/remote-clusters/eck-remote-clusters.md + title: "To the same ECK environment" + - page: docs-content://deploy-manage/remote-clusters/eck-remote-clusters-to-other-eck.md + title: "To a different ECK environment" + - page: docs-content://deploy-manage/remote-clusters/eck-remote-clusters-to-external.md + title: "To an external cluster or deployment" + - group: Cross-project search + page: docs-content://deploy-manage/cross-project-search-config.md + children: + - page: docs-content://deploy-manage/cross-project-search-config/cps-config-link-and-manage.md + title: Link and manage projects + - page: docs-content://deploy-manage/cross-project-search-config/cps-config-access-and-scope.md + title: Access and scope + - page: docs-content://deploy-manage/cloud-connect.md + title: Cloud Connect + - label: ─── + - group: Maintenance + page: docs-content://deploy-manage/maintenance.md + children: + - page: docs-content://deploy-manage/maintenance/ece.md + title: ECE maintenance + children: + - page: docs-content://deploy-manage/maintenance/ece/deployments-maintenance.md + title: Deployments maintenance + children: + - page: docs-content://deploy-manage/maintenance/ece/pause-instance.md + title: Pause instance + - page: docs-content://deploy-manage/maintenance/ece/maintenance-activities.md + title: Maintenance activities + children: + - page: docs-content://deploy-manage/maintenance/ece/enable-maintenance-mode.md + title: Enable maintenance mode + - page: docs-content://deploy-manage/maintenance/ece/scale-out-installation.md + title: Scale out your installation + - page: docs-content://deploy-manage/maintenance/ece/move-nodes-instances-from-allocators.md + title: Move nodes or instances from allocators + - page: docs-content://deploy-manage/maintenance/ece/perform-ece-hosts-maintenance.md + title: Perform ECE hosts maintenance + - page: docs-content://deploy-manage/maintenance/ece/delete-ece-hosts.md + title: Delete ECE hosts + - page: docs-content://deploy-manage/maintenance/start-stop-services.md + title: Start and stop services + children: + - page: docs-content://deploy-manage/maintenance/start-stop-services/start-stop-elasticsearch.md + title: Start and stop Elasticsearch + - page: docs-content://deploy-manage/maintenance/start-stop-services/start-stop-kibana.md + title: Start and stop Kibana + - page: docs-content://deploy-manage/maintenance/start-stop-services/restart-cloud-hosted-deployment.md + title: Restart an Elastic Cloud Hosted deployment + - page: docs-content://deploy-manage/maintenance/start-stop-services/restart-an-ece-deployment.md + title: Restart an ECE deployment + - page: docs-content://deploy-manage/maintenance/start-stop-services/full-cluster-restart-rolling-restart-procedures.md + title: Full Cluster restart and rolling restart procedures + - page: docs-content://deploy-manage/maintenance/start-stop-routing-requests.md + title: Start and stop routing requests + - page: docs-content://deploy-manage/maintenance/add-and-remove-elasticsearch-nodes.md + title: Add and Remove Elasticsearch nodes + - group: Upgrade + page: docs-content://deploy-manage/upgrade.md + children: + - page: docs-content://deploy-manage/upgrade/plan-upgrade.md + title: Plan your upgrade + - page: docs-content://deploy-manage/upgrade/prepare-to-upgrade.md + title: "Preparation steps" + children: + - page: docs-content://deploy-manage/upgrade/prepare-to-upgrade/upgrade-assistant.md + title: Upgrade Assistant + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster.md + title: Upgrade your deployment or cluster + children: + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/upgrade-717.md + title: Upgrade from 7.17 to latest + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/upgrade-on-ech.md + title: "Upgrade on Elastic Cloud Hosted" + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/upgrade-on-ece.md + title: "Upgrade on Elastic Cloud Enterprise" + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/upgrade-on-eck.md + title: "Upgrade on Elastic Cloud on Kubernetes" + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/self-managed.md + title: "Upgrade Elastic on a self-managed cluster" + children: + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/elasticsearch.md + title: Upgrade Elasticsearch + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/upgrade-elasticsearch-docker.md + title: "Upgrade Elasticsearch running on Docker" + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/kibana.md + title: Upgrade Kibana + children: + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/saved-object-migrations.md + title: Saved object migrations + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/kibana-roll-back.md + title: "Roll back to a previous version" + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/archived-settings.md + title: "Archived settings" + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/reading-indices-from-older-elasticsearch-versions.md + title: "Reading indices from older versions" + - page: docs-content://deploy-manage/upgrade/deployment-or-cluster/enterprise-search.md + title: Upgrade Enterprise Search + - page: docs-content://deploy-manage/upgrade/ingest-components.md + title: Upgrade your ingest components + - page: docs-content://deploy-manage/upgrade/orchestrator.md + title: "Upgrade your ECE or ECK orchestrator" + children: + - page: docs-content://deploy-manage/upgrade/orchestrator/upgrade-cloud-enterprise.md + title: Upgrade Elastic Cloud Enterprise + children: + - page: docs-content://deploy-manage/upgrade/orchestrator/re-running-the-ece-upgrade.md + title: Re-running the ECE upgrade + - page: docs-content://deploy-manage/upgrade/orchestrator/upgrade-cloud-on-k8s.md + title: Upgrade Elastic Cloud on Kubernetes + - group: Uninstall + page: docs-content://deploy-manage/uninstall.md + children: + - page: docs-content://deploy-manage/uninstall/uninstall-elastic-cloud-enterprise.md + title: Uninstall Elastic Cloud Enterprise + - page: docs-content://deploy-manage/uninstall/uninstall-elastic-cloud-on-kubernetes.md + title: Uninstall Elastic Cloud on Kubernetes + - page: docs-content://deploy-manage/uninstall/delete-a-cloud-deployment.md + title: "Delete an orchestrated deployment" + - group: Licenses and subscriptions + page: docs-content://deploy-manage/license.md + children: + - page: docs-content://deploy-manage/license/manage-your-license-in-ece.md + title: "Elastic Cloud Enterprise" + - page: docs-content://deploy-manage/license/manage-your-license-in-eck.md + title: "Elastic Cloud on Kubernetes" + - page: docs-content://deploy-manage/license/manage-your-license-in-self-managed-cluster.md + title: "Self-managed cluster" + - group: Manage your Cloud organization + page: docs-content://deploy-manage/cloud-organization.md + children: + - page: docs-content://deploy-manage/cloud-organization/billing.md + title: Billing + children: + - page: docs-content://deploy-manage/cloud-organization/billing/cloud-hosted-deployment-billing-dimensions.md + title: "Hosted billing dimensions" + - page: docs-content://deploy-manage/cloud-organization/billing/serverless-project-billing-dimensions.md + title: "Serverless billing dimensions" + children: + - page: docs-content://deploy-manage/cloud-organization/billing/elasticsearch-billing-dimensions.md + title: "Elasticsearch projects" + - page: docs-content://deploy-manage/cloud-organization/billing/elastic-observability-billing-dimensions.md + title: "Observability projects" + - page: docs-content://deploy-manage/cloud-organization/billing/security-billing-dimensions.md + title: "Security projects" + - page: docs-content://deploy-manage/cloud-organization/billing/billing-models.md + title: Billing models + - page: docs-content://deploy-manage/cloud-organization/billing/add-billing-details.md + title: Add your billing details + - page: docs-content://deploy-manage/cloud-organization/billing/view-billing-history.md + title: View your billing history + - page: docs-content://deploy-manage/cloud-organization/billing/manage-billing-notifications.md + title: "Manage notifications" + - page: docs-content://deploy-manage/cloud-organization/billing/manage-subscription.md + title: Manage your subscription + - page: docs-content://deploy-manage/cloud-organization/billing/monitor-analyze-usage.md + title: Monitor and analyze usage + - page: docs-content://deploy-manage/cloud-organization/billing/ecu.md + title: Elastic Consumption Units + - page: docs-content://deploy-manage/cloud-organization/billing/billing-faq.md + title: Billing FAQ + - page: docs-content://deploy-manage/cloud-organization/operational-emails.md + title: Operational emails + - page: docs-content://deploy-manage/cloud-organization/billing/update-billing-operational-contacts.md + title: Update billing and operational contacts + - page: docs-content://deploy-manage/cloud-organization/service-status.md + title: Service status + - page: docs-content://deploy-manage/cloud-organization/tools-and-apis.md + title: "Tools and APIs" + - group: Deployment and administration tools + children: + - toc: ecctl://reference + - page: elasticsearch://reference/elasticsearch/command-line-tools/index.md + title: Command-line tools + - label: The Elasticsearch platform + children: + - label: Ingest and manage data + children: + - group: "Ingest or migrate: bring your data into Elasticsearch" + children: + - page: docs-content://manage-data/ingest.md + title: Choose/Plan your ingest method + - group: Ingest architectures + page: docs-content://manage-data/ingest/ingest-reference-architectures.md + children: + - group: Agent to Elasticsearch + page: docs-content://manage-data/ingest/ingest-reference-architectures/agent-to-es.md + children: + - page: docs-content://manage-data/ingest/ingest-reference-architectures/agent-installed.md + title: Elastic Agent (installed) + - page: docs-content://manage-data/ingest/ingest-reference-architectures/agent-apis.md + title: Elastic Agent via APIs + - group: Agent with Logstash + page: docs-content://manage-data/ingest/ingest-reference-architectures/agent-ls.md + children: + - page: docs-content://manage-data/ingest/ingest-reference-architectures/ls-enrich.md + title: Logstash for enrichment + - page: docs-content://manage-data/ingest/ingest-reference-architectures/lspq.md + title: Logstash persistent queue + - page: docs-content://manage-data/ingest/ingest-reference-architectures/ls-networkbridge.md + title: Logstash as a network bridge + - page: docs-content://manage-data/ingest/ingest-reference-architectures/ls-multi.md + title: Logstash for multiple destinations + - page: docs-content://manage-data/ingest/ingest-reference-architectures/agent-proxy.md + title: Agent through a proxy + - group: Agent with Kafka + page: docs-content://manage-data/ingest/ingest-reference-architectures/agent-kafka-es.md + children: + - page: docs-content://manage-data/ingest/ingest-reference-architectures/agent-kafka-ls.md + title: Agent to Kafka via Logstash + - page: docs-content://manage-data/ingest/ingest-reference-architectures/agent-kafka-essink.md + title: Agent to Kafka with Elasticsearch sink + - page: docs-content://manage-data/ingest/ingest-reference-architectures/ls-for-input.md + title: Logstash as input + - group: Air-gapped environments + page: docs-content://manage-data/ingest/ingest-reference-architectures/airgapped-env.md + children: + - page: docs-content://manage-data/ingest/ingest-reference-architectures/agent-es-airgapped.md + title: Agent to Elasticsearch (air-gapped) + - page: docs-content://manage-data/ingest/ingest-reference-architectures/agent-ls-airgapped.md + title: Agent with Logstash (air-gapped) + - group: Ingest by solution + page: docs-content://manage-data/ingest/ingesting-data-for-elastic-solutions.md + children: + - page: docs-content://solutions/search/ingest-for-search.md + title: Ingesting data for search use cases + - title: Ingesting data for observability + - page: docs-content://solutions/security/get-started/ingest-data-to-elastic-security.md + title: Ingesting data for security (Move from Solutions) + - page: docs-content://manage-data/ingest/ingesting-timeseries-data.md + title: Ingesting time series data + - group: Ingest logs + page: docs-content://solutions/observability/logs.md + children: + - page: docs-content://solutions/observability/logs/get-started-with-system-logs.md + title: Get started with system logs + - page: docs-content://solutions/observability/logs/stream-any-log-file.md + title: Stream any log file + - page: docs-content://solutions/observability/logs/stream-any-log-file-using-edot-collector.md + title: Stream any log file using EDOT Collector + - group: Stream application logs + page: docs-content://solutions/observability/logs/stream-application-logs.md + children: + - page: docs-content://solutions/observability/logs/plaintext-application-logs.md + title: Plaintext application logs + - page: docs-content://solutions/observability/logs/ecs-formatted-application-logs.md + title: ECS formatted application logs + - page: docs-content://solutions/observability/logs/apm-agent-log-sending.md + title: APM agent log sending + - page: docs-content://solutions/observability/logs/parse-route-logs.md + title: Parse and route logs + - page: docs-content://solutions/observability/logs/filter-aggregate-logs.md + title: Filter and aggregate logs + - group: Explore logs + page: docs-content://solutions/observability/logs/explore-logs.md + children: + - page: docs-content://solutions/observability/logs/discover-logs.md + title: Discover logs + - page: docs-content://solutions/observability/logs/categorize-log-entries.md + title: Categorize log entries + - page: docs-content://solutions/observability/logs/inspect-log-anomalies.md + title: Inspect log anomalies + - page: docs-content://solutions/observability/logs/run-pattern-analysis-on-log-data.md + title: Run pattern analysis on log data + - page: docs-content://solutions/observability/logs/log-data-sources.md + title: Log data sources + - page: docs-content://solutions/observability/logs/logs-data-retention.md + title: Logs data retention + - page: docs-content://solutions/observability/logs/add-service-name-to-logs.md + title: Add service name to logs + - group: Logs index template reference + page: docs-content://solutions/observability/logs/logs-index-template-reference.md + children: + - page: docs-content://solutions/observability/logs/logs-index-template-defaults.md + title: Logs index template defaults + - group: Ingest data with agentless integrations + page: docs-content://manage-data/ingest/agentless/agentless-integrations.md + children: + - page: docs-content://manage-data/ingest/agentless/cloud-connector-deployment.md + title: Cloud connector deployment + - page: docs-content://manage-data/ingest/agentless/agentless-integrations-faq.md + title: Agentless integrations FAQ + - page: integration-docs://reference/agentless_integrations.md + title: Agentless integrations reference + - group: Ingest data from applications + page: docs-content://manage-data/ingest/ingesting-data-from-applications.md + children: + - toc: docs-content://reference/apm-agents + - page: docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-data-with-nodejs-on-elasticsearch-service.md + title: Ingest data with Node.js + - page: docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-data-with-python-on-elasticsearch-service.md + title: Ingest data with Python + - page: docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-data-from-beats-to-elasticsearch-service-with-logstash-as-proxy.md + title: Ingest data from Beats with Logstash as a proxy + - page: docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-data-from-relational-database-into-elasticsearch-service.md + title: Ingest data from a relational database + - page: docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-logs-from-python-application-using-filebeat.md + title: Ingest logs from a Python application using Filebeat + - page: docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-logs-from-nodejs-web-application-using-filebeat.md + title: Ingest logs from a Node.js web application using Filebeat + - title: Ingest data using the API + - page: docs-content://manage-data/ingest/tools.md + title: Ingest tools overview + - page: docs-content://manage-data/ingest/upload-data-files.md + title: Upload data files + - page: docs-content://manage-data/ingest/sample-data.md + title: Sample data + - group: Migrating your Elasticsearch data + page: docs-content://manage-data/migrate.md + children: + - page: docs-content://manage-data/migrate/migrate-from-a-self-managed-cluster-with-a-self-signed-certificate-using-remote-reindex.md + title: Reindex using a private CA + - page: docs-content://manage-data/migrate/migrate-with-logstash.md + title: Migrate Elastic Cloud Hosted data to Serverless with Logstash + - page: docs-content://manage-data/migrate/migrate-data-between-elasticsearch-clusters-with-minimal-downtime.md + title: Minimal-downtime migration using snapshots + - page: docs-content://manage-data/migrate/migrate-internal-indices.md + title: Migrate system indices + - group: Ingest tools + page: docs-content://reference/ingestion-tools/index.md + children: + - toc: opentelemetry://reference + - toc: docs-content://reference/fleet + - toc: integration-docs://reference + - toc: elasticsearch://reference/search-connectors + - toc: logstash://reference + - toc: docs-content://reference/apm + - toc: beats://reference + - group: Other ingest tools + children: + - toc: elasticsearch-hadoop://reference + - toc: elastic-serverless-forwarder://reference + - group: Data storage and lifecycle + children: + - group: The Elasticsearch data store + page: docs-content://manage-data/data-store.md + children: + - group: Index basics + page: docs-content://manage-data/data-store/index-basics.md + children: + - page: elasticsearch://reference/elasticsearch/index-settings/index.md + title: Index settings reference + - page: elasticsearch://reference/elasticsearch/index-lifecycle-actions/index.md + title: Index lifecycle management actions + - page: docs-content://manage-data/data-store/near-real-time-search.md + title: Near real-time search + - group: Data streams + page: docs-content://manage-data/data-store/data-streams.md + children: + - page: docs-content://manage-data/data-store/data-streams/set-up-data-stream.md + title: Set up a data stream + - page: docs-content://manage-data/data-store/data-streams/use-data-stream.md + title: Use a data stream + - page: docs-content://manage-data/data-store/data-streams/modify-data-stream.md + title: Modify a data stream + - page: docs-content://manage-data/data-store/data-streams/manage-data-stream.md + title: Manage a data stream + - group: Time series data stream (TSDS) + page: docs-content://manage-data/data-store/data-streams/time-series-data-stream-tsds.md + children: + - page: docs-content://manage-data/data-store/data-streams/quickstart-tsds.md + title: Quickstart + - page: docs-content://manage-data/data-store/data-streams/set-up-tsds.md + title: Set up TSDS + - group: Downsampling + page: docs-content://manage-data/data-store/data-streams/downsampling-time-series-data-stream.md + children: + - page: docs-content://manage-data/data-store/data-streams/downsampling-concepts.md + title: Downsampling concepts + - page: docs-content://manage-data/data-store/data-streams/run-downsampling.md + title: Run downsampling + - page: docs-content://manage-data/data-store/data-streams/query-downsampled-data.md + title: Query downsampled data + - group: Advanced topics + page: docs-content://manage-data/data-store/data-streams/advanced-topics-tsds.md + children: + - page: docs-content://manage-data/data-store/data-streams/time-bound-tsds.md + title: Time-bound TSDS + - page: docs-content://manage-data/data-store/data-streams/reindex-tsds.md + title: Reindex TSDS + - page: docs-content://manage-data/data-store/data-streams/tsds-ingest-otlp.md + title: TSDS ingest OTLP + - group: Logs data stream + page: docs-content://manage-data/data-store/data-streams/logs-data-stream.md + children: + - page: docs-content://manage-data/data-store/data-streams/logs-data-stream-configure.md + title: Configure + - page: docs-content://manage-data/data-store/data-streams/logs-data-stream-integrations.md + title: Integrations + - group: Failure store + page: docs-content://manage-data/data-store/data-streams/failure-store.md + children: + - page: docs-content://manage-data/data-store/data-streams/failure-store-recipes.md + title: Recipes + - group: Mapping + page: docs-content://manage-data/data-store/mapping.md + children: + - page: elasticsearch://reference/elasticsearch/mapping-reference/index.md + title: Mapping reference + - group: Dynamic mapping + page: docs-content://manage-data/data-store/mapping/dynamic-mapping.md + children: + - page: docs-content://manage-data/data-store/mapping/dynamic-field-mapping.md + title: Dynamic field mapping + - page: docs-content://manage-data/data-store/mapping/dynamic-templates.md + title: Dynamic templates + - page: docs-content://manage-data/data-store/mapping/explicit-mapping.md + title: Explicit mapping + - group: Runtime fields + page: docs-content://manage-data/data-store/mapping/runtime-fields.md + children: + - page: docs-content://manage-data/data-store/mapping/map-runtime-field.md + title: Map a runtime field + - page: docs-content://manage-data/data-store/mapping/define-runtime-fields-in-search-request.md + title: Define in search request + - page: docs-content://manage-data/data-store/mapping/override-field-values-at-query-time.md + title: Override values at query time + - page: docs-content://manage-data/data-store/mapping/retrieve-runtime-field.md + title: Retrieve a runtime field + - page: docs-content://manage-data/data-store/mapping/index-runtime-field.md + title: Index a runtime field + - page: docs-content://manage-data/data-store/mapping/explore-data-with-runtime-fields.md + title: Explore data with runtime fields + - page: docs-content://manage-data/data-store/mapping/removal-of-mapping-types.md + title: Removal of mapping types + - page: docs-content://manage-data/data-store/mapping/update-mappings-examples.md + title: Update mappings examples + - group: Text analysis + page: docs-content://manage-data/data-store/text-analysis.md + children: + - page: elasticsearch://reference/text-analysis/index.md + title: Text analysis components + - group: Concepts + page: docs-content://manage-data/data-store/text-analysis/concepts.md + children: + - page: docs-content://manage-data/data-store/text-analysis/anatomy-of-an-analyzer.md + title: Anatomy of an analyzer + - page: docs-content://manage-data/data-store/text-analysis/index-search-analysis.md + title: Index and search analysis + - page: docs-content://manage-data/data-store/text-analysis/stemming.md + title: Stemming + - page: docs-content://manage-data/data-store/text-analysis/token-graphs.md + title: Token graphs + - group: Configure text analysis + page: docs-content://manage-data/data-store/text-analysis/configure-text-analysis.md + children: + - page: docs-content://manage-data/data-store/text-analysis/test-an-analyzer.md + title: Test an analyzer + - page: docs-content://manage-data/data-store/text-analysis/configuring-built-in-analyzers.md + title: Configuring built-in analyzers + - page: docs-content://manage-data/data-store/text-analysis/create-custom-analyzer.md + title: Create custom analyzer + - page: docs-content://manage-data/data-store/text-analysis/specify-an-analyzer.md + title: Specify an analyzer + - group: Templates + page: docs-content://manage-data/data-store/templates.md + children: + - page: docs-content://manage-data/data-store/templates/simulate-multi-component-templates.md + title: Simulate multi-component templates + - page: docs-content://manage-data/data-store/templates/ignore-missing-component-templates.md + title: Ignore missing component templates + - page: docs-content://manage-data/data-store/aliases.md + title: Aliases + - group: Perform index operations + page: docs-content://manage-data/data-store/perform-index-operations.md + children: + - page: docs-content://manage-data/data-store/index-operations-reference.md + title: Index operations reference + - page: docs-content://manage-data/data-store/manage-data-from-the-command-line.md + title: Manage data from the command line + - group: Data lifecycle + page: docs-content://manage-data/lifecycle.md + children: + - page: docs-content://manage-data/lifecycle/data-tiers.md + title: "Data tiers: hot warm cold frozen" + - group: Index lifecycle management (ILM) + page: docs-content://manage-data/lifecycle/index-lifecycle-management.md + children: + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/index-lifecycle.md + title: Index lifecycle + - group: Rollover + page: docs-content://manage-data/lifecycle/index-lifecycle-management/rollover.md + children: + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/skip-rollover.md + title: Skip rollover + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/configure-lifecycle-policy.md + title: Configure lifecycle policy + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/policy-apply.md + title: Apply policy + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/policy-view-status.md + title: View policy status + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/manage-lifecycle-integrations-data.md + title: Manage integrations data + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/policy-updates.md + title: Policy updates + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/start-stop-index-lifecycle-management.md + title: Start and stop ILM + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/restore-managed-data-stream-index.md + title: Restore managed index + - group: ILM tutorials + page: docs-content://manage-data/lifecycle/index-lifecycle-management/ilm-tutorials.md + children: + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/tutorial-time-series-with-data-streams.md + title: Time series with data streams + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/tutorial-time-series-without-data-streams.md + title: Time series without data streams + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/tutorial-general-content-with-data-streams.md + title: General content with data streams + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/tutorial-customize-built-in-policies.md + title: Customize built-in policies + - group: Migrate ILM + page: docs-content://manage-data/lifecycle/index-lifecycle-management/migrate-ilm.md + children: + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/migrate-index-management.md + title: Migrate index management + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/manage-existing-indices.md + title: Manage existing indices + - page: docs-content://manage-data/lifecycle/index-lifecycle-management/migrate-index-allocation-filters-to-node-roles.md + title: Migrate allocation filters to node roles + - group: Data stream lifecycle + page: docs-content://manage-data/lifecycle/data-stream.md + children: + - page: docs-content://manage-data/lifecycle/data-stream/tutorial-create-data-stream-with-lifecycle.md + title: "Tutorial: Create with lifecycle" + - page: docs-content://manage-data/lifecycle/data-stream/tutorial-update-existing-data-stream.md + title: "Tutorial: Update existing" + - page: docs-content://manage-data/lifecycle/data-stream/tutorial-data-stream-retention.md + title: "Tutorial: Retention" + - page: docs-content://manage-data/lifecycle/data-stream/tutorial-migrate-ilm-managed-data-stream-to-data-stream-lifecycle.md + title: "Tutorial: Migrate from ILM" + - page: docs-content://manage-data/lifecycle/curator.md + title: Elasticsearch Curator + - group: Rollup + page: docs-content://manage-data/lifecycle/rollup.md + children: + - page: docs-content://manage-data/lifecycle/rollup/getting-started-api.md + title: Getting started (API) + - page: docs-content://manage-data/lifecycle/rollup/getting-started-kibana.md + title: Getting started (Kibana) + - page: docs-content://manage-data/lifecycle/rollup/understanding-groups.md + title: Understanding groups + - page: docs-content://manage-data/lifecycle/rollup/rollup-aggregation-limitations.md + title: Aggregation limitations + - page: docs-content://manage-data/lifecycle/rollup/rollup-search-limitations.md + title: Search limitations + - page: docs-content://manage-data/lifecycle/rollup/migrating-from-rollup-to-downsampling.md + title: Migrating to downsampling + - page: docs-content://manage-data/use-case-use-elasticsearch-to-manage-time-series-data.md + title: "Use case: time series data management" + - group: Transform and enrich data + page: docs-content://manage-data/ingest/transform-enrich.md + children: + - group: Data pipelines + children: + - page: opentelemetry://reference/motlp/index.md + title: Managed pipelines (mOTLP) + - group: Elasticsearch ingest pipelines + page: docs-content://manage-data/ingest/transform-enrich/ingest-pipelines.md + children: + - page: docs-content://manage-data/ingest/transform-enrich/example-parse-logs.md + title: "Example: Parse logs" + - page: docs-content://manage-data/ingest/transform-enrich/readable-maintainable-ingest-pipelines.md + title: Readable maintainable pipelines + - page: docs-content://manage-data/ingest/transform-enrich/error-handling.md + title: Error handling + - page: docs-content://manage-data/ingest/transform-enrich/logstash-pipelines.md + title: Logstash pipelines + - group: Data enrichment + page: docs-content://manage-data/ingest/transform-enrich/data-enrichment.md + children: + - page: docs-content://manage-data/ingest/transform-enrich/set-up-an-enrich-processor.md + title: Set up an enrich processor + - page: docs-content://manage-data/ingest/transform-enrich/example-enrich-data-based-on-geolocation.md + title: "Example: Enrich by geolocation" + - page: docs-content://manage-data/ingest/transform-enrich/example-enrich-data-based-on-exact-values.md + title: "Example: Enrich by exact values" + - page: docs-content://manage-data/ingest/transform-enrich/example-enrich-data-by-matching-value-to-range.md + title: "Example: Enrich by matching value to range" + - page: docs-content://manage-data/ingest/transform-enrich/index-mapping-text-analysis.md + title: Index mapping and text analysis + - page: docs-content://manage-data/ingest/transform-enrich/ingest-lag.md + title: Calculate ingest lag metadata + - label: Search, visualize and analyze + children: + # ======================================================================= + # SEARCH AND QUERY + # Narrative arc: get started → approaches → ingest → querying → cross-cluster → integrate + # Pages pulled from solutions/search/ and explore-analyze/ at current paths. + # [from E&A] = page currently lives in explore-analyze/ + # [TODO] = placeholder, page not yet written + # ======================================================================= + - group: Search and query + page: docs-content://solutions/search.md + children: + + # ----------------------------------------------------------------------- + # 1. GET STARTED + # ----------------------------------------------------------------------- + - group: Get started with search + page: docs-content://solutions/search/get-started.md + children: + - group: Quickstarts + page: docs-content://solutions/search/get-started/quickstarts.md + children: + - page: docs-content://solutions/search/get-started/index-basics.md + title: Index basics + - page: docs-content://solutions/search/get-started/keyword-search-python.md + title: Keyword search with Python + - page: docs-content://solutions/search/get-started/semantic-search.md + title: Semantic search + + # ----------------------------------------------------------------------- + # 2. SEARCH APPROACHES + # What kind of search do I need? + # FTS is the foundation — everything else layers on top. + # ----------------------------------------------------------------------- + - group: Search approaches + page: docs-content://solutions/search/search-approaches.md + children: + # Full-text is core to every deployment, not just one option + - group: Full-text search + page: docs-content://solutions/search/full-text.md + children: + - page: docs-content://solutions/search/full-text/how-full-text-works.md + title: How full-text search works + - page: docs-content://solutions/search/full-text/text-analysis-during-search.md + title: Text analysis during search + - group: Search relevance + page: docs-content://solutions/search/full-text/search-relevance.md + children: + - page: docs-content://solutions/search/full-text/search-relevance/mixing-exact-search-with-stemming.md + title: Mix exact search with stemming + - page: docs-content://solutions/search/full-text/search-relevance/consistent-scoring.md + title: Consistent scoring + - page: docs-content://solutions/search/full-text/search-relevance/static-scoring-signals.md + title: Static scoring signals + - group: Synonyms + page: docs-content://solutions/search/full-text/search-with-synonyms.md + children: + - page: docs-content://solutions/search/full-text/create-update-synonyms-api-example.md + title: Create and update synonyms via API + # Vector search — formerly "AI search" + - group: Vector search + page: docs-content://solutions/search/ai-search/ai-search.md + children: + - group: Dense vector search + page: docs-content://solutions/search/vector/dense-vector.md + children: + - page: docs-content://solutions/search/vector/knn.md + title: k-nearest neighbor (kNN) + - page: docs-content://solutions/search/vector/bring-own-vectors.md + title: Bring your own vectors + - page: docs-content://solutions/search/vector/sparse-vector.md + title: Sparse vector search + - page: docs-content://solutions/search/vector/dense-versus-sparse-ingest-pipelines.md + title: Dense vs sparse ingest pipelines + - group: Semantic search + page: docs-content://solutions/search/semantic-search.md + children: + - page: docs-content://solutions/search/semantic-search/semantic-search-semantic-text.md + title: Semantic search with semantic_text + - page: docs-content://solutions/search/semantic-search/semantic-search-inference.md + title: Semantic search with the inference API + - page: docs-content://solutions/search/semantic-search/semantic-search-elser-ingest-pipelines.md + title: Semantic search with ELSER + - page: docs-content://solutions/search/semantic-search/cohere-es.md + title: Semantic search with Cohere + - page: docs-content://solutions/search/using-openai-compatible-models.md + title: Using OpenAI-compatible models + - group: Hybrid search + page: docs-content://solutions/search/hybrid-search.md + children: + - page: docs-content://solutions/search/hybrid-semantic-text.md + title: Hybrid search with semantic_text + # Ranking — semantic reranking is a lightweight upgrade on top of FTS + - group: Ranking and reranking + page: docs-content://solutions/search/ranking.md + children: + - page: docs-content://solutions/search/ranking/semantic-reranking.md + title: Semantic reranking + - group: Learning to rank + page: docs-content://solutions/search/ranking/learning-to-rank-ltr.md + children: + - page: docs-content://solutions/search/ranking/learning-to-rank-model-training.md + title: Train an LTR model + - page: docs-content://solutions/search/ranking/learning-to-rank-search-usage.md + title: Use LTR in search + + # ----------------------------------------------------------------------- + # 3. INGEST FOR SEARCH + # After approaches — the approach decision shapes what your pipeline needs. + # ----------------------------------------------------------------------- + - group: Ingest for search + page: docs-content://solutions/search/ingest-for-search.md + children: + - page: docs-content://solutions/search/search-pipelines.md + title: Search pipelines + + # ----------------------------------------------------------------------- + # 4. QUERYING + # How do I express and execute my search? + # Two dimensions: what language? what interface? + # ----------------------------------------------------------------------- + - group: Build search queries + page: docs-content://solutions/search/querying-for-search.md + children: + - group: The search API + page: docs-content://solutions/search/the-search-api.md + children: + - page: docs-content://solutions/search/search-templates.md + title: Search templates + - group: Aggregations + page: docs-content://explore-analyze/query-filter/aggregations.md # [from E&A] + children: + - page: docs-content://explore-analyze/query-filter/aggregations/tutorial-analyze-ecommerce-data-with-aggregations-using-query-dsl.md + title: Tutorial — analyze e-commerce data + - page: docs-content://solutions/search/retrievers-overview.md + title: Retrievers + - page: docs-content://solutions/search/esql-for-search.md + title: ES|QL for search + - page: docs-content://solutions/search/async-search-api.md + title: Async search + # Language narrative pages — "understand and decide" layer + # These sit above the bulk reference toc further down. + - group: Query languages + page: docs-content://explore-analyze/query-filter/languages.md # [from E&A] comparison table landing + children: + - page: docs-content://explore-analyze/query-filter/languages/querydsl.md # [from E&A] + title: Query DSL + - page: docs-content://explore-analyze/query-filter/languages/kql.md # [from E&A] + title: KQL + - group: EQL + page: docs-content://explore-analyze/query-filter/languages/eql.md # [from E&A] + children: + - page: docs-content://explore-analyze/query-filter/languages/example-detect-threats-with-eql.md + title: Detect threats with EQL + # [THIN] sql.md is a hollow intro + link list that duplicates the reference toc below. + # Candidate for removal — redirect to elasticsearch://reference/query-languages/sql.md + - page: docs-content://explore-analyze/query-filter/languages/sql.md # [from E&A] + title: SQL + # [THIN] lucene-query-syntax.md is a single paragraph pointing users to the Kibana UI toggle. + # Candidate for removal — redirect to elasticsearch://reference/query-languages/query-dsl/query-dsl-query-string-query.md + - page: docs-content://explore-analyze/query-filter/languages/lucene-query-syntax.md # [from E&A] + title: Lucene query syntax + # Query language reference — bulk toc, cannot interleave with narrative pages above + - toc: elasticsearch://reference/query-languages + # Developer tools — for running and testing queries + - group: Developer tools + page: docs-content://explore-analyze/query-filter/tools.md # [from E&A] + children: + - page: docs-content://explore-analyze/query-filter/tools/console.md + title: Console + - page: docs-content://explore-analyze/query-filter/tools/search-profiler.md + title: Search Profiler + - page: docs-content://explore-analyze/query-filter/tools/saved-queries.md + title: Saved queries + + # ----------------------------------------------------------------------- + # 5. SEARCH ACROSS CLUSTERS AND PROJECTS + # End-of-journey scaling topic: "my data spans multiple clusters" + # ----------------------------------------------------------------------- + - group: Cross-cluster search + page: docs-content://explore-analyze/cross-cluster-search.md # [from E&A] + children: + - page: docs-content://explore-analyze/cross-cluster-search/using-resolve-cluster-endpoint-before-cross-cluster-search.md + title: Using the resolve cluster endpoint + # Cross-project search — serverless only, pages are hidden in toc + - group: Cross-project search + page: docs-content://explore-analyze/cross-project-search.md # [from E&A] + children: + - page: docs-content://explore-analyze/cross-project-search/cross-project-search-link-projects.md + title: Link projects + - page: docs-content://explore-analyze/cross-project-search/cross-project-search-search.md + title: Search across projects + - page: docs-content://explore-analyze/cross-project-search/cross-project-search-tags.md + title: Using tags to control search + - page: docs-content://explore-analyze/cross-project-search/cross-project-search-project-routing.md + title: Project routing + + # ----------------------------------------------------------------------- + # 6. INTEGRATE WITH YOUR APP + # Wire search into your application code. + # [TODO] site-or-app.md → integrate.md rename deferred to docs-content work + # ----------------------------------------------------------------------- + - group: Integrate with your app + page: docs-content://solutions/search/site-or-app.md + children: + - page: docs-content://solutions/search/site-or-app/clients.md + title: Client libraries + - page: docs-content://solutions/search/site-or-app/search-ui.md + title: Search UI + - page: docs-content://solutions/search/apis-and-tools.md + title: APIs and tools + - group: Explore and visualize + page: docs-content://explore-analyze/explore-and-visualize.md + children: + - page: docs-content://explore-analyze/kibana-data-exploration-learning-tutorial.md + title: Learn data exploration and visualization + - group: Discover + page: docs-content://explore-analyze/discover.md + children: + - page: docs-content://explore-analyze/discover/discover-get-started.md + title: Explore fields and data with Discover + - page: docs-content://explore-analyze/discover/document-explorer.md + title: Customize the Discover view + - page: docs-content://explore-analyze/discover/discover-search-for-relevance.md + title: Search for relevance + - page: docs-content://explore-analyze/discover/save-open-search.md + title: Save a search for reuse + - page: docs-content://explore-analyze/discover/show-field-statistics.md + title: View field statistics + - page: docs-content://explore-analyze/discover/run-pattern-analysis-discover.md + title: Run a pattern analysis on your log data + - page: docs-content://explore-analyze/discover/background-search.md + title: Run queries in the background + - page: docs-content://explore-analyze/discover/try-esql.md + title: "Using ES|QL" + - group: Dashboards + page: docs-content://explore-analyze/dashboards.md + children: + - page: docs-content://explore-analyze/dashboards/using.md + title: Exploring dashboards + - group: Building dashboards + page: docs-content://explore-analyze/dashboards/building.md + children: + - page: docs-content://explore-analyze/dashboards/create-dashboard.md + title: Create a dashboard + - page: docs-content://explore-analyze/dashboards/open-dashboard.md + title: Edit a dashboard + - page: docs-content://explore-analyze/dashboards/add-controls.md + title: Add filter controls + - page: docs-content://explore-analyze/dashboards/drilldowns.md + title: Add drilldowns + - page: docs-content://explore-analyze/dashboards/arrange-panels.md + title: Organize dashboard panels + - page: docs-content://explore-analyze/dashboards/duplicate-dashboards.md + title: Duplicate a dashboard + - page: docs-content://explore-analyze/dashboards/import-dashboards.md + title: Import a dashboard + - page: docs-content://explore-analyze/dashboards/managing.md + title: Managing dashboards + - page: docs-content://explore-analyze/dashboards/sharing.md + title: Sharing dashboards + - group: Tutorials + page: docs-content://explore-analyze/dashboards/tutorials.md + children: + - page: docs-content://explore-analyze/dashboards/create-dashboard-of-panels-with-web-server-data.md + title: Create a simple dashboard to monitor website logs + - page: docs-content://explore-analyze/dashboards/create-dashboard-of-panels-with-ecommerce-data.md + title: Create a dashboard with time series charts + - group: Panels and visualizations + page: docs-content://explore-analyze/visualize.md + children: + - page: docs-content://explore-analyze/visualize/visualize-library.md + title: Visualize Library + - page: docs-content://explore-analyze/visualize/manage-panels.md + title: Manage panels + - group: Lens + page: docs-content://explore-analyze/visualize/lens.md + children: + - page: docs-content://explore-analyze/visualize/charts/area-charts.md + title: Area charts + - page: docs-content://explore-analyze/visualize/charts/bar-charts.md + title: Bar charts + - page: docs-content://explore-analyze/visualize/charts/heat-map-charts.md + title: Heat map charts + - page: docs-content://explore-analyze/visualize/charts/gauge-charts.md + title: Gauge charts + - page: docs-content://explore-analyze/visualize/charts/line-charts.md + title: Line charts + - page: docs-content://explore-analyze/visualize/charts/metric-charts.md + title: Metric charts + - page: docs-content://explore-analyze/visualize/charts/mosaic-charts.md + title: Mosaic charts + - page: docs-content://explore-analyze/visualize/charts/pie-charts.md + title: Pie charts + - page: docs-content://explore-analyze/visualize/charts/region-map-charts.md + title: Region map charts + - page: docs-content://explore-analyze/visualize/charts/tables.md + title: Tables + - page: docs-content://explore-analyze/visualize/charts/waffle-charts.md + title: Waffle charts + - page: docs-content://explore-analyze/visualize/charts/tag-cloud-charts.md + title: Tag cloud charts + - page: docs-content://explore-analyze/visualize/charts/treemap-charts.md + title: Treemap charts + - page: docs-content://explore-analyze/visualize/esorql.md + title: "ES|QL" + - page: docs-content://explore-analyze/visualize/custom-visualizations-with-vega.md + title: Custom visualizations with Vega + - page: docs-content://explore-analyze/visualize/text-panels.md + title: Text panels + - page: docs-content://explore-analyze/visualize/image-panels.md + title: Image panels + - page: docs-content://explore-analyze/visualize/link-panels.md + title: Link panels + - page: docs-content://explore-analyze/visualize/alert-panels.md + title: Alert panels + - group: Canvas + page: docs-content://explore-analyze/visualize/canvas.md + children: + - page: docs-content://explore-analyze/visualize/canvas/edit-workpads.md + title: Edit workpads + - page: docs-content://explore-analyze/visualize/canvas/canvas-present-workpad.md + title: Present your workpad + - page: docs-content://explore-analyze/visualize/canvas/canvas-tutorial.md + title: "Tutorial: Create a workpad for monitoring sales" + - group: Canvas function reference + page: docs-content://explore-analyze/visualize/canvas/canvas-function-reference.md + children: + - page: docs-content://explore-analyze/visualize/canvas/canvas-tinymath-functions.md + title: TinyMath functions + - group: Maps + page: docs-content://explore-analyze/visualize/maps.md + children: + - page: docs-content://explore-analyze/visualize/maps/maps-getting-started.md + title: Build a map to compare metrics by country or region + - page: docs-content://explore-analyze/visualize/maps/asset-tracking-tutorial.md + title: Track, visualize, and alert on assets in real time + - page: docs-content://explore-analyze/visualize/maps/reverse-geocoding-tutorial.md + title: Map custom regions with reverse geocoding + - page: docs-content://explore-analyze/visualize/maps/heatmap-layer.md + title: Heat map layer + - page: docs-content://explore-analyze/visualize/maps/tile-layer.md + title: Tile layer + - group: Vector layer + page: docs-content://explore-analyze/visualize/maps/vector-layer.md + children: + - page: docs-content://explore-analyze/visualize/maps/vector-style.md + title: Vector styling + - page: docs-content://explore-analyze/visualize/maps/maps-vector-style-properties.md + title: Vector style properties + - page: docs-content://explore-analyze/visualize/maps/vector-tooltip.md + title: Vector tooltips + - group: Map aggregations + page: docs-content://explore-analyze/visualize/maps/maps-aggregations.md + children: + - page: docs-content://explore-analyze/visualize/maps/maps-grid-aggregation.md + title: Clusters + - page: docs-content://explore-analyze/visualize/maps/maps-top-hits-aggregation.md + title: Display the most relevant documents per entity + - page: docs-content://explore-analyze/visualize/maps/point-to-point.md + title: Point to point + - page: docs-content://explore-analyze/visualize/maps/terms-join.md + title: Term join + - group: Search and filter maps + page: docs-content://explore-analyze/visualize/maps/maps-search.md + children: + - page: docs-content://explore-analyze/visualize/maps/maps-create-filter-from-map.md + title: Create filters from a map + - page: docs-content://explore-analyze/visualize/maps/maps-layer-based-filtering.md + title: Filter a single layer + - page: docs-content://explore-analyze/visualize/maps/maps-search-across-multiple-indices.md + title: Search across multiple indices + - page: docs-content://explore-analyze/visualize/maps/maps-settings.md + title: Configure map settings + - page: docs-content://explore-analyze/visualize/maps/maps-connect-to-ems.md + title: Connect to Elastic Maps Service + - group: Import geospatial data + page: docs-content://explore-analyze/visualize/maps/import-geospatial-data.md + children: + - page: docs-content://explore-analyze/visualize/maps/maps-clean-data.md + title: Clean your data + - page: docs-content://explore-analyze/visualize/maps/indexing-geojson-data-tutorial.md + title: "Tutorial: Index GeoJSON data" + - page: docs-content://explore-analyze/visualize/maps/maps-troubleshooting.md + title: Troubleshoot + - group: Graph + page: docs-content://explore-analyze/visualize/graph.md + children: + - page: docs-content://explore-analyze/visualize/graph/graph-configuration.md + title: Configure Graph + - page: docs-content://explore-analyze/visualize/graph/graph-troubleshooting.md + title: Troubleshooting and limitations + - group: Legacy editors + page: docs-content://explore-analyze/visualize/legacy-editors.md + children: + - page: docs-content://explore-analyze/visualize/legacy-editors/aggregation-based.md + title: Aggregation-based + - page: docs-content://explore-analyze/visualize/legacy-editors/tsvb.md + title: TSVB + - page: docs-content://explore-analyze/visualize/legacy-editors/timelion.md + title: Timelion + - group: Find and organize content + page: docs-content://explore-analyze/find-and-organize.md + children: + - page: docs-content://explore-analyze/find-and-organize/data-views.md + title: Data views + - page: docs-content://explore-analyze/find-and-organize/saved-objects.md + title: Saved objects + - page: docs-content://explore-analyze/find-and-organize/files.md + title: Files + - page: docs-content://explore-analyze/find-and-organize/reports.md + title: Reports + - page: docs-content://explore-analyze/find-and-organize/tags.md + title: Tags + - page: docs-content://explore-analyze/find-and-organize/find-apps-and-objects.md + title: Find apps and objects + - group: Track and respond + page: docs-content://explore-analyze/track-and-respond.md + children: + - group: Reporting and sharing + page: docs-content://explore-analyze/report-and-share.md + children: + - page: docs-content://explore-analyze/report-and-share/automating-report-generation.md + title: Automatically generate reports + - group: Reporting troubleshooting + page: docs-content://explore-analyze/report-and-share/reporting-troubleshooting.md + children: + - page: docs-content://explore-analyze/report-and-share/reporting-troubleshooting-csv.md + title: CSV + - page: docs-content://explore-analyze/report-and-share/reporting-troubleshooting-pdf.md + title: PDF/PNG + - group: Alerting + page: docs-content://explore-analyze/alerting.md + children: + - group: Alerts + page: docs-content://explore-analyze/alerting/alerts.md + children: + - page: docs-content://explore-analyze/alerting/alerts/alerting-getting-started.md + title: Getting started with alerts + - page: docs-content://explore-analyze/alerting/alerts/alerting-setup.md + title: Set up + - page: docs-content://explore-analyze/alerting/alerts/create-manage-rules.md + title: Create and manage rules + - page: docs-content://explore-analyze/alerting/alerts/view-alerts.md + title: View and manage alerts + - page: docs-content://explore-analyze/alerting/alerts/query-alerts.md + title: Query alert indices + - group: Rule types + page: docs-content://explore-analyze/alerting/alerts/rule-types.md + children: + - page: docs-content://explore-analyze/alerting/alerts/rule-type-index-threshold.md + title: Index threshold + - page: docs-content://explore-analyze/alerting/alerts/rule-type-es-query.md + title: Elasticsearch query + - page: docs-content://explore-analyze/alerting/alerts/geo-alerting.md + title: Tracking containment + - page: docs-content://explore-analyze/alerting/alerts/rule-action-variables.md + title: Rule action variables + - page: docs-content://explore-analyze/alerting/alerts/notifications-domain-allowlist.md + title: Notifications domain allowlist + - group: Alerting troubleshooting + page: docs-content://explore-analyze/alerting/alerts/alerting-troubleshooting.md + children: + - page: docs-content://explore-analyze/alerting/alerts/alerting-common-issues.md + title: Common issues + - page: docs-content://explore-analyze/alerting/alerts/event-log-index.md + title: Event log index + - page: docs-content://explore-analyze/alerting/alerts/testing-connectors.md + title: Test connectors + - page: docs-content://explore-analyze/alerting/alerts/maintenance-windows.md + title: Maintenance windows + - group: Watcher + page: docs-content://explore-analyze/alerting/watcher.md + children: + - page: docs-content://explore-analyze/alerting/watcher/watcher-getting-started.md + title: Getting started with Watcher + - page: docs-content://explore-analyze/alerting/watcher/how-watcher-works.md + title: How Watcher works + - page: docs-content://explore-analyze/alerting/watcher/enable-watcher.md + title: Enable Watcher + - page: docs-content://explore-analyze/alerting/watcher/watcher-ui.md + title: Watcher UI + - page: docs-content://explore-analyze/alerting/watcher/encrypting-data.md + title: Encrypting sensitive data in Watcher + - group: Input + page: docs-content://explore-analyze/alerting/watcher/input.md + children: + - page: docs-content://explore-analyze/alerting/watcher/input-simple.md + title: Simple input + - page: docs-content://explore-analyze/alerting/watcher/input-search.md + title: Search input + - page: docs-content://explore-analyze/alerting/watcher/input-http.md + title: HTTP input + - page: docs-content://explore-analyze/alerting/watcher/input-chain.md + title: Chain input + - group: Trigger + page: docs-content://explore-analyze/alerting/watcher/trigger.md + children: + - page: docs-content://explore-analyze/alerting/watcher/trigger-schedule.md + title: Schedule trigger + - page: docs-content://explore-analyze/alerting/watcher/throttling.md + title: Throttling + - page: docs-content://explore-analyze/alerting/watcher/schedule-types.md + title: Schedule Types + - group: Condition + page: docs-content://explore-analyze/alerting/watcher/condition.md + children: + - page: docs-content://explore-analyze/alerting/watcher/condition-always.md + title: Always condition + - page: docs-content://explore-analyze/alerting/watcher/condition-never.md + title: Never condition + - page: docs-content://explore-analyze/alerting/watcher/condition-compare.md + title: Compare condition + - page: docs-content://explore-analyze/alerting/watcher/condition-array-compare.md + title: Array compare condition + - page: docs-content://explore-analyze/alerting/watcher/condition-script.md + title: Script condition + - group: Actions + page: docs-content://explore-analyze/alerting/watcher/actions.md + children: + - page: docs-content://explore-analyze/alerting/watcher/action-foreach.md + title: Running an action for each element in an array + - page: docs-content://explore-analyze/alerting/watcher/action-conditions.md + title: Adding conditions to actions + - page: docs-content://explore-analyze/alerting/watcher/actions-email.md + title: Email action + - page: docs-content://explore-analyze/alerting/watcher/actions-webhook.md + title: Webhook action + - page: docs-content://explore-analyze/alerting/watcher/actions-index.md + title: Index action + - page: docs-content://explore-analyze/alerting/watcher/actions-logging.md + title: Logging action + - page: docs-content://explore-analyze/alerting/watcher/actions-slack.md + title: Slack action + - page: docs-content://explore-analyze/alerting/watcher/actions-pagerduty.md + title: PagerDuty action + - page: docs-content://explore-analyze/alerting/watcher/actions-jira.md + title: Jira action + - group: Transform + page: docs-content://explore-analyze/alerting/watcher/transform.md + children: + - page: docs-content://explore-analyze/alerting/watcher/transform-search.md + title: Search payload transform + - page: docs-content://explore-analyze/alerting/watcher/transform-script.md + title: Script payload transform + - page: docs-content://explore-analyze/alerting/watcher/transform-chain.md + title: Chain payload transform + - page: docs-content://explore-analyze/alerting/watcher/managing-watches.md + title: Managing watches + - group: Example watches + page: docs-content://explore-analyze/alerting/watcher/example-watches.md + children: + - page: docs-content://explore-analyze/alerting/watcher/watch-cluster-status.md + title: Watching the status of an Elasticsearch cluster + - page: docs-content://explore-analyze/alerting/watcher/execute-watch.md + title: Execute a watch + - page: docs-content://explore-analyze/alerting/watcher/watcher-limitations.md + title: Limitations + - group: Cases + page: docs-content://explore-analyze/cases.md + children: + - page: docs-content://explore-analyze/cases/control-case-access.md + title: Control access + - page: docs-content://explore-analyze/cases/create-cases.md + title: Create cases + - page: docs-content://explore-analyze/cases/manage-cases.md + title: Manage cases + - page: docs-content://explore-analyze/cases/attach-objects-to-cases.md + title: Attach objects + - page: docs-content://explore-analyze/cases/search-share-cases.md + title: Search and share + - page: docs-content://explore-analyze/cases/configure-case-settings.md + title: Configure settings + - page: docs-content://explore-analyze/cases/cases-as-data.md + title: Cases as data + - label: Automate + children: + - group: Workflows + page: docs-content://explore-analyze/workflows.md + children: + - page: docs-content://explore-analyze/workflows/setup.md + title: Set up workflows + - page: docs-content://explore-analyze/workflows/get-started.md + title: Get started with workflows + - group: Core components + page: docs-content://explore-analyze/workflows/core-components.md + children: + - group: Triggers + page: docs-content://explore-analyze/workflows/triggers.md + children: + - page: docs-content://explore-analyze/workflows/triggers/manual-triggers.md + title: Manual triggers + - page: docs-content://explore-analyze/workflows/triggers/scheduled-triggers.md + title: Scheduled triggers + - page: docs-content://explore-analyze/workflows/triggers/alert-triggers.md + title: Alert triggers + - group: Steps + page: docs-content://explore-analyze/workflows/steps.md + children: + - group: Action steps + page: docs-content://explore-analyze/workflows/steps/action-steps.md + children: + - page: docs-content://explore-analyze/workflows/steps/elasticsearch.md + title: Elasticsearch + - page: docs-content://explore-analyze/workflows/steps/kibana.md + title: Kibana + - page: docs-content://explore-analyze/workflows/steps/external-systems-apps.md + title: External systems and apps + - group: Flow control steps + page: docs-content://explore-analyze/workflows/steps/flow-control-steps.md + children: + - page: docs-content://explore-analyze/workflows/steps/if.md + title: If + - page: docs-content://explore-analyze/workflows/steps/foreach.md + title: Foreach + - page: docs-content://explore-analyze/workflows/steps/wait.md + title: Wait + - page: docs-content://explore-analyze/workflows/steps/ai-steps.md + title: AI steps + - group: Data and error handling + page: docs-content://explore-analyze/workflows/data.md + children: + - page: docs-content://explore-analyze/workflows/data/templating.md + title: Templating engine + - page: docs-content://explore-analyze/workflows/author-workflows.md + title: Author workflows + - page: docs-content://explore-analyze/workflows/monitor-troubleshoot.md + title: Monitor and troubleshoot workflows + - page: docs-content://explore-analyze/workflows/manage-workflows.md + title: Manage workflows + - page: docs-content://explore-analyze/workflows/templates.md + title: Workflow templates + - label: AI and machine learning + children: + - group: Machine Learning and NLP + page: docs-content://explore-analyze/machine-learning.md + children: + - page: docs-content://explore-analyze/machine-learning/setting-up-machine-learning.md + title: Setup and security + - group: Anomaly detection + page: docs-content://explore-analyze/machine-learning/anomaly-detection.md + children: + - group: Finding anomalies + page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-ad-finding-anomalies.md + children: + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-ad-plan.md + title: Plan your analysis + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-ad-run-jobs.md + title: Run a job + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-ad-view-results.md + title: View the results + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-ad-forecast.md + title: Forecast future behavior + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-getting-started.md + title: Tutorial + - group: Concepts + page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-ad-concepts.md + children: + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-ad-algorithms.md + title: Anomaly detection algorithms + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-ad-explain.md + title: Anomaly score explanation + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-anomaly-detection-job-types.md + title: Job types + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/anomaly-detection-scale.md + title: Working with anomaly detection at scale + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-delayed-data-detection.md + title: Handling delayed data + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-api-quickref.md + title: API quick reference + - group: How-tos + page: docs-content://explore-analyze/machine-learning/anomaly-detection/anomaly-how-tos.md + children: + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-configuring-alerts.md + title: Generating alerts for anomaly detection jobs + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-configuring-aggregation.md + title: Aggregating data for faster performance + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-configuring-transform.md + title: Altering data in your datafeed with runtime fields + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-configuring-detector-custom-rules.md + title: Customizing detectors with custom rules + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-configuring-categories.md + title: Detecting anomalous categories of data + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-configuring-populations.md + title: Performing population analysis + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-reverting-model-snapshot.md + title: Reverting to a model snapshot + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/geographic-anomalies.md + title: Detecting anomalous locations in geographic data + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/mapping-anomalies.md + title: Mapping anomalies by location + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-configuring-url.md + title: Adding custom URLs to machine learning results + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-jobs-from-lens.md + title: Anomaly detection jobs from visualizations + - group: Resources + page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-ad-resources.md + children: + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-limitations.md + title: Limitations + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-functions.md + title: Analysis function reference + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ootb-ml-jobs.md + title: Supplied configurations + - page: docs-content://explore-analyze/machine-learning/anomaly-detection/ml-ad-troubleshooting.md + title: Troubleshooting and FAQ + - group: Data frame analytics + page: docs-content://explore-analyze/machine-learning/data-frame-analytics.md + children: + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfa-overview.md + title: Overview + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfa-finding-outliers.md + title: Finding outliers + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfa-regression.md + title: Predicting numerical values with regression + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfa-classification.md + title: Predicting classes with classification + - group: Concepts + page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfa-concepts.md + children: + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfa-phases.md + title: How data frame analytics jobs work + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfa-scale.md + title: Working with data frame analytics at scale + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfa-custom-urls.md + title: Adding custom URLs to data frame analytics jobs + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-feature-encoding.md + title: Feature encoding + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-feature-processors.md + title: Feature processors + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-feature-importance.md + title: Feature importance + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/dfa-regression-lossfunction.md + title: Loss functions for regression analyses + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/hyperparameters.md + title: Hyperparameter optimization + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-trained-models.md + title: Trained models + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfanalytics-apis.md + title: API quick reference + - group: Resources + page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfa-resources.md + children: + - page: docs-content://explore-analyze/machine-learning/data-frame-analytics/ml-dfa-limitations.md + title: Limitations + - group: NLP + page: docs-content://explore-analyze/machine-learning/nlp.md + children: + - group: Overview + page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-overview.md + children: + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-extract-info.md + title: Extract information + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-classify-text.md + title: Classify text + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-search-compare.md + title: Search and compare text + - group: Deploy models + page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-deploy-models.md + children: + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-select-model.md + title: Select a trained model + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-import-model.md + title: Import the trained model and vocabulary + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-deploy-model.md + title: Deploy the model in your cluster + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-test-inference.md + title: Try it out + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-inference.md + title: Add NLP inference to ingest pipelines + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-apis.md + title: API quick reference + - group: Built-in models + page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-built-in-models.md + children: + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-elser.md + title: ELSER + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-jina.md + title: Jina + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-rerank.md + title: Elastic Rerank + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-e5.md + title: E5 + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-lang-ident.md + title: Language identification + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-model-ref.md + title: Compatible third party models + - group: Examples + page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-examples.md + children: + - page: docs-content://explore-analyze/machine-learning/nlp/nlp-end-to-end-tutorial.md + title: End-to-end tutorial + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-ner-example.md + title: Named entity recognition + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-text-emb-vector-search-example.md + title: Text embedding and semantic search + - page: docs-content://explore-analyze/machine-learning/nlp/ml-nlp-limitations.md + title: Limitations + - group: Machine learning in Kibana + page: docs-content://explore-analyze/machine-learning/machine-learning-in-kibana.md + children: + - page: docs-content://explore-analyze/machine-learning/machine-learning-in-kibana/xpack-ml-aiops.md + title: AIOps Labs + - page: docs-content://explore-analyze/machine-learning/machine-learning-in-kibana/inference-processing.md + title: Inference processing + - group: Agent builder + page: docs-content://explore-analyze/ai-features/elastic-agent-builder.md + children: + - page: docs-content://explore-analyze/ai-features/agent-builder/get-started.md + title: Get started + - page: docs-content://explore-analyze/ai-features/agent-builder/models.md + title: Models + - group: Chat + page: docs-content://explore-analyze/ai-features/agent-builder/chat.md + children: + - page: docs-content://explore-analyze/ai-features/agent-builder/standalone-and-flyout-modes.md + title: Chat UI modes + - group: Agents + page: docs-content://explore-analyze/ai-features/agent-builder/agent-builder-agents.md + children: + - page: docs-content://explore-analyze/ai-features/agent-builder/custom-agents.md + title: Custom agents + - page: docs-content://explore-analyze/ai-features/agent-builder/builtin-agents-reference.md + title: Built-in agents + - page: docs-content://explore-analyze/ai-features/agent-builder/prompt-engineering.md + title: Prompting best practices + - page: docs-content://explore-analyze/ai-features/agent-builder/agents-and-workflows.md + title: Call agents from workflows + - group: Tools + page: docs-content://explore-analyze/ai-features/agent-builder/tools.md + children: + - page: docs-content://explore-analyze/ai-features/agent-builder/tools/builtin-tools-reference.md + title: Built-in tools + - group: Custom tools + page: docs-content://explore-analyze/ai-features/agent-builder/tools/custom-tools.md + children: + - page: docs-content://explore-analyze/ai-features/agent-builder/tools/esql-tools.md + title: "ES|QL tools" + - page: docs-content://explore-analyze/ai-features/agent-builder/tools/index-search-tools.md + title: Index search tools + - page: docs-content://explore-analyze/ai-features/agent-builder/tools/mcp-tools.md + title: MCP tools + - page: docs-content://explore-analyze/ai-features/agent-builder/tools/workflow-tools.md + title: Workflow tools + - group: Programmatic access + page: docs-content://explore-analyze/ai-features/agent-builder/programmatic-access.md + children: + - group: Kibana API + page: docs-content://explore-analyze/ai-features/agent-builder/kibana-api.md + children: + - page: docs-content://explore-analyze/ai-features/agent-builder/agent-builder-api-tutorial.md + title: Kibana API tutorial + - page: docs-content://explore-analyze/ai-features/agent-builder/a2a-server.md + title: A2A server + - page: docs-content://explore-analyze/ai-features/agent-builder/mcp-server.md + title: MCP server + - page: docs-content://explore-analyze/ai-features/agent-builder/monitor-usage.md + title: Monitor token usage + - page: docs-content://explore-analyze/ai-features/agent-builder/permissions.md + title: Permissions + - group: Troubleshooting + page: docs-content://explore-analyze/ai-features/agent-builder/troubleshooting.md + children: + - page: docs-content://explore-analyze/ai-features/agent-builder/troubleshooting/context-length-exceeded.md + title: Context length exceeded + - page: docs-content://explore-analyze/ai-features/agent-builder/troubleshooting/api-calls-return-403-forbidden.md + title: 403 Forbidden + - page: docs-content://explore-analyze/ai-features/agent-builder/limitations-known-issues.md + title: Limitations + - group: Elastic Inference Service + page: docs-content://explore-analyze/elastic-inference.md + children: + - group: Elastic Inference Service + page: docs-content://explore-analyze/elastic-inference/eis.md + children: + - page: docs-content://explore-analyze/elastic-inference/connect-self-managed-cluster-to-eis.md + title: EIS for self-managed clusters + - page: docs-content://explore-analyze/elastic-inference/inference-api.md + title: Inference integrations + - group: AI chat and LLM configuration + page: docs-content://explore-analyze/ai-features.md + children: + - group: AI chat experiences + page: docs-content://explore-analyze/ai-features/ai-chat-experiences.md + children: + - page: docs-content://explore-analyze/ai-features/ai-chat-experiences/ai-agent-or-ai-assistant.md + title: Compare Agent Builder and AI Assistant + - page: docs-content://explore-analyze/ai-features/ai-chat-experiences/ai-assistant.md + title: AI assistants + - group: LLM providers + page: docs-content://explore-analyze/ai-features/llm-guides/llm-connectors.md + children: + - page: docs-content://explore-analyze/ai-features/llm-guides/connect-to-azure-openai.md + title: Connect to Azure OpenAI + - page: docs-content://explore-analyze/ai-features/llm-guides/connect-to-amazon-bedrock.md + title: Connect to Amazon Bedrock + - page: docs-content://explore-analyze/ai-features/llm-guides/connect-to-openai.md + title: Connect to OpenAI + - page: docs-content://explore-analyze/ai-features/llm-guides/connect-to-google-vertex.md + title: Connect to Google Vertex + - group: Local LLMs + page: docs-content://explore-analyze/ai-features/llm-guides/local-llms-overview.md + children: + - page: docs-content://explore-analyze/ai-features/llm-guides/connect-to-lmstudio-observability.md + title: Connect to LM Studio for Observability + - page: docs-content://explore-analyze/ai-features/llm-guides/connect-to-lmstudio-security.md + title: Connect to LM Studio for Elastic Security + - page: docs-content://explore-analyze/ai-features/llm-guides/connect-to-vLLM.md + title: Connect to vLLM for Elastic Security + - page: docs-content://explore-analyze/ai-features/manage-access-to-ai-assistant.md + title: Manage access to AI features + - page: docs-content://explore-analyze/ai-features/agent-skills.md + title: AI agent skills for Elastic + - label: Solutions and project types + children: + - page: docs-content://solutions/index.md + title: Solutions overview + - group: Elasticsearch solution + page: docs-content://solutions/elasticsearch-solution-project.md + children: + - group: Get started + page: docs-content://solutions/elasticsearch-solution-project/get-started.md + children: + - page: docs-content://solutions/elasticsearch-solution-project/search-connection-details.md + title: Find connection details + - group: Playground + page: docs-content://solutions/elasticsearch-solution-project/playground.md + children: + - page: docs-content://solutions/elasticsearch-solution-project/playground-context.md + title: Optimize model context + - page: docs-content://solutions/elasticsearch-solution-project/playground-query.md + title: View and modify queries + - page: docs-content://solutions/elasticsearch-solution-project/playground-troubleshooting.md + title: Troubleshooting + - page: docs-content://solutions/elasticsearch-solution-project/ai-assistant.md + title: AI Assistant + - page: docs-content://solutions/elasticsearch-solution-project/query-rules-ui.md + title: Query rules UI + - group: Search Applications + page: docs-content://solutions/elasticsearch-solution-project/search-applications.md + children: + - page: docs-content://solutions/elasticsearch-solution-project/search-applications/search-application-api.md + title: Search API and templates + - page: docs-content://solutions/elasticsearch-solution-project/search-applications/search-application-security.md + title: Security + - page: docs-content://solutions/elasticsearch-solution-project/search-applications/search-application-client.md + title: Search Application client guide + - page: docs-content://solutions/elasticsearch-solution-project/es-serverless-add-ons.md + title: Add-ons + - group: Observability solution + page: docs-content://solutions/observability.md + children: + - group: Get started + page: docs-content://solutions/observability/get-started.md + children: + - group: Elastic Observability quickstarts + page: docs-content://solutions/observability/get-started/quickstarts.md + children: + - page: docs-content://solutions/observability/get-started/quickstart-monitor-hosts-with-opentelemetry.md + title: "Quickstart: Monitor hosts with OpenTelemetry" + - page: docs-content://solutions/observability/get-started/quickstart-monitor-your-application-performance.md + title: "Quickstart: Monitor your application performance" + - page: docs-content://solutions/observability/get-started/quickstart-unified-kubernetes-observability-with-elastic-distributions-of-opentelemetry-edot.md + title: "Quickstart: Unified Kubernetes Observability with Elastic Distributions of OpenTelemetry (EDOT)" + - page: docs-content://solutions/observability/get-started/quickstart-elastic-cloud-otel-endpoint.md + title: "Quickstart: Send OTLP data to Elastic Serverless or Elastic Cloud Hosted" + - page: docs-content://solutions/observability/get-started/quickstart-create-synthetic-monitor.md + title: "Quickstart: Create a Synthetic Monitor" + - page: docs-content://solutions/observability/get-started/quickstart-monitor-hosts-with-elastic-agent.md + title: "Quickstart: Monitor hosts with Elastic Agent" + - page: docs-content://solutions/observability/get-started/quickstart-monitor-kubernetes-cluster-with-elastic-agent.md + title: "Quickstart: Monitor your Kubernetes cluster with Elastic Agent" + - page: docs-content://solutions/observability/get-started/quickstart-collect-data-with-aws-firehose.md + title: "Quickstart: Collect data with AWS Firehose" + - group: OpenTelemetry quickstarts + page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/index.md + children: + - page: docs-content://solutions/observability/get-started/opentelemetry/custom-metrics-quickstart.md + title: Ingest custom metrics with EDOT + - group: Self-managed + page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/self-managed/index.md + children: + - page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/self-managed/k8s.md + title: Kubernetes + - page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/self-managed/hosts_vms.md + title: Hosts / VMs + - page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/self-managed/docker.md + title: Docker + - group: Elastic Cloud Serverless + page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/serverless/index.md + children: + - page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/serverless/k8s.md + title: Kubernetes + - page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/serverless/hosts_vms.md + title: Hosts and VMs + - page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/serverless/docker.md + title: Docker + - group: Elastic Cloud Hosted + page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/ech/index.md + children: + - page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/ech/k8s.md + title: Kubernetes + - page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/ech/hosts_vms.md + title: Hosts and VMs + - page: docs-content://solutions/observability/get-started/opentelemetry/quickstart/ech/docker.md + title: Docker + - group: OpenTelemetry use cases + page: docs-content://solutions/observability/get-started/opentelemetry/use-cases/index.md + children: + - group: Kubernetes observability + page: docs-content://solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md + children: + - page: docs-content://solutions/observability/get-started/opentelemetry/use-cases/kubernetes/prerequisites-compatibility.md + title: Prerequisites and compatibility + - page: docs-content://solutions/observability/get-started/opentelemetry/use-cases/kubernetes/components.md + title: Components description + - page: docs-content://solutions/observability/get-started/opentelemetry/use-cases/kubernetes/deployment.md + title: Deployment + - page: docs-content://solutions/observability/get-started/opentelemetry/use-cases/kubernetes/instrumenting-applications.md + title: Instrumenting Applications + - page: docs-content://solutions/observability/get-started/opentelemetry/use-cases/kubernetes/upgrade.md + title: Upgrade + - page: docs-content://solutions/observability/get-started/opentelemetry/use-cases/kubernetes/customization.md + title: Customization + - page: docs-content://solutions/observability/get-started/opentelemetry/use-cases/llms/index.md + title: LLM observability + - group: Other Observability tutorials + page: docs-content://solutions/observability/get-started/other-tutorials/index.md + children: + - page: docs-content://solutions/observability/get-started/other-tutorials/tutorial-monitor-java-application.md + title: "Tutorial: Monitor a Java application" + - page: docs-content://solutions/observability/get-started/logs-essentials.md + title: Logs Essentials + - group: Applications and services + page: docs-content://solutions/observability/applications/index.md + children: + - group: Application performance monitoring (APM) + page: docs-content://solutions/observability/apm/index.md + children: + - page: docs-content://solutions/observability/apm/get-started.md + title: Get started with traces and APM + - group: Application data types + page: docs-content://solutions/observability/apm/data-types.md + children: + - page: docs-content://solutions/observability/apm/spans.md + title: Spans + - group: Transactions + page: docs-content://solutions/observability/apm/transactions.md + children: + - page: docs-content://solutions/observability/apm/transaction-sampling.md + title: Transaction sampling + - page: docs-content://solutions/observability/apm/traces.md + title: Traces + - page: docs-content://solutions/observability/apm/errors.md + title: Errors + - page: docs-content://solutions/observability/apm/metrics.md + title: Metrics + - page: docs-content://solutions/observability/apm/metadata.md + title: Metadata + - group: Collect application data + page: docs-content://solutions/observability/apm/ingest/index.md + children: + - group: OpenTelemetry + page: docs-content://solutions/observability/apm/opentelemetry/index.md + children: + - page: docs-content://solutions/observability/apm/opentelemetry/upstream-opentelemetry-collectors-language-sdks.md + title: Contrib OpenTelemetry Collectors and language SDKs + - page: docs-content://solutions/observability/apm/opentelemetry/collect-metrics.md + title: Collect metrics + - page: docs-content://solutions/observability/apm/opentelemetry/create-apm-agent-key-for-edot-sdks.md + title: Create APM agent key for EDOT SDKs + - page: docs-content://solutions/observability/apm/opentelemetry/edot-sdks-central-configuration.md + title: Centrally configure EDOT SDKs + - page: docs-content://solutions/observability/apm/opentelemetry/limitations.md + title: Limitations + - page: docs-content://solutions/observability/apm/opentelemetry/attributes.md + title: Attributes and labels + - page: docs-content://solutions/observability/apm/opentelemetry/data-stream-routing.md + title: Data stream routing + - group: APM agents + page: docs-content://solutions/observability/apm/apm-agents/index.md + children: + - page: docs-content://solutions/observability/apm/apm-agents/central-configuration.md + title: Centrally configure APM agents + - page: docs-content://solutions/observability/apm/apm-agents/real-user-monitoring-rum.md + title: Real User Monitoring (RUM) + - page: docs-content://solutions/observability/apm/apm-agents/create-upload-source-maps-rum.md + title: Create and upload source maps (RUM) + - page: docs-content://solutions/observability/apm/ingest/apm-k8s-attacher.md + title: Kubernetes + - page: docs-content://solutions/observability/apm/ingest/monitor-aws-lambda-functions.md + title: AWS Lambda Functions + - page: docs-content://solutions/observability/apm/ingest/jaeger.md + title: Jaeger (deprecated) + - group: View and analyze data + page: docs-content://solutions/observability/apm/view-analyze-data.md + children: + - group: Overviews + page: docs-content://solutions/observability/apm/overviews.md + children: + - page: docs-content://solutions/observability/apm/services.md + title: Services + - page: docs-content://solutions/observability/apm/traces-ui.md + title: Traces UI + - page: docs-content://solutions/observability/apm/dependencies.md + title: Dependencies + - page: docs-content://solutions/observability/apm/service-map.md + title: Service Map + - page: docs-content://solutions/observability/apm/service-overview.md + title: Service overview + - page: docs-content://solutions/observability/apm/mobile-service-overview.md + title: Mobile service overview + - group: Drill down into data + page: docs-content://solutions/observability/apm/drill-down-into-data.md + children: + - page: docs-content://solutions/observability/apm/transactions-ui.md + title: Transactions UI + - page: docs-content://solutions/observability/apm/trace-sample-timeline.md + title: Trace sample timeline + - page: docs-content://solutions/observability/apm/errors-ui.md + title: Errors UI + - page: docs-content://solutions/observability/apm/metrics-ui.md + title: Metrics UI + - page: docs-content://solutions/observability/apm/infrastructure.md + title: Infrastructure + - page: docs-content://solutions/observability/apm/logs.md + title: Logs + - page: docs-content://solutions/observability/apm/discover-traces.md + title: Discover traces + - group: Filter and search data + page: docs-content://solutions/observability/apm/filter-search-data.md + children: + - page: docs-content://solutions/observability/apm/filter-data.md + title: Filters + - page: docs-content://solutions/observability/apm/advanced-queries.md + title: Advanced queries + - page: docs-content://solutions/observability/apm/cross-cluster-search.md + title: Cross-cluster search + - group: Interpret data + page: docs-content://solutions/observability/apm/interpret-data.md + children: + - page: docs-content://solutions/observability/apm/find-transaction-latency-failure-correlations.md + title: Find transaction latency and failure correlations + - page: docs-content://solutions/observability/apm/track-deployments-with-annotations.md + title: Track deployments with annotations + - page: docs-content://solutions/observability/apm/explore-mobile-sessions.md + title: Explore mobile sessions with Discover + - page: docs-content://solutions/observability/apm/observe-lambda-functions.md + title: Observe Lambda functions + - page: docs-content://solutions/observability/apm/machine-learning.md + title: Integrate with machine learning + - page: docs-content://solutions/observability/apm/apm-agent-explorer.md + title: APM Agent explorer + - page: docs-content://solutions/observability/apm/applications-ui-settings.md + title: Settings + - group: Act on data + page: docs-content://solutions/observability/apm/act-on-data.md + children: + - page: docs-content://solutions/observability/apm/create-apm-rules-alerts.md + title: Create rules and alerts + - page: docs-content://solutions/observability/apm/create-custom-links.md + title: Create custom links + - group: Use APM securely + page: docs-content://solutions/observability/apm/use-apm-securely.md + children: + - group: Secure data + page: docs-content://solutions/observability/apm/secure-data.md + children: + - page: docs-content://solutions/observability/apm/control-access-to-apm-data.md + title: Control access to APM data + - page: docs-content://solutions/observability/apm/built-in-data-filters.md + title: Built-in data filters + - page: docs-content://solutions/observability/apm/custom-filters.md + title: Custom filters + - page: docs-content://solutions/observability/apm/delete-sensitive-data.md + title: Delete sensitive data + - group: Secure communication with APM agents + page: docs-content://solutions/observability/apm/secure-communication-with-apm-agents.md + children: + - page: docs-content://solutions/observability/apm/apm-agent-tls-communication.md + title: APM agent TLS communication + - page: docs-content://solutions/observability/apm/api-keys.md + title: API keys + - page: docs-content://solutions/observability/apm/secret-token.md + title: Secret token + - page: docs-content://solutions/observability/apm/anonymous-authentication.md + title: Anonymous authentication + - group: Secure communication with the Elastic Stack + page: docs-content://solutions/observability/apm/secure-communication-with-elastic-stack.md + children: + - page: docs-content://solutions/observability/apm/create-assign-feature-roles-to-apm-server-users.md + title: Use feature roles + - page: docs-content://solutions/observability/apm/grant-access-using-api-keys.md + title: Grant access using API keys + - group: Secure access to the Applications UI + page: docs-content://solutions/observability/apm/secure-access-to-applications-ui.md + children: + - page: docs-content://solutions/observability/apm/ui-user-reader.md + title: Create an APM reader user + - page: docs-content://solutions/observability/apm/ui-user-annotation.md + title: Create an annotation user + - page: docs-content://solutions/observability/apm/ui-user-api.md + title: Create an API user + - page: docs-content://solutions/observability/apm/ui-user-central-config.md + title: Create a central config user + - page: docs-content://solutions/observability/apm/ui-user-storage-explorer.md + title: Create a storage explorer user + - group: Manage storage + page: docs-content://solutions/observability/apm/manage-storage.md + children: + - page: docs-content://solutions/observability/apm/storage-explorer.md + title: Storage Explorer + - page: docs-content://solutions/observability/apm/data-streams.md + title: Data streams + - page: docs-content://solutions/observability/apm/index-lifecycle-management.md + title: Index lifecycle management + - page: docs-content://solutions/observability/apm/view-elasticsearch-index-template.md + title: View the Elasticsearch index template + - page: docs-content://solutions/observability/apm/parse-data-using-ingest-pipelines.md + title: Parse data using ingest pipelines + - page: docs-content://solutions/observability/apm/storage-sizing-guide.md + title: Storage and sizing guide + - page: docs-content://solutions/observability/apm/reduce-storage.md + title: Reduce storage + - page: docs-content://solutions/observability/apm/explore-data-in-elasticsearch.md + title: Explore data in Elasticsearch + - group: "Work with APM Server" + page: docs-content://solutions/observability/apm/apm-server/index.md + children: + - group: Set up + page: docs-content://solutions/observability/apm/apm-server/setup.md + children: + - page: docs-content://solutions/observability/apm/apm-server/fleet-managed.md + title: Fleet-managed APM Server + - page: docs-content://solutions/observability/apm/apm-server/binary.md + title: APM Server binary + - group: Configure + page: docs-content://solutions/observability/apm/apm-server/configure.md + children: + - page: docs-content://solutions/observability/apm/apm-server/general-configuration-options.md + title: General configuration options + - page: docs-content://solutions/observability/apm/apm-server/configure-anonymous-authentication.md + title: Anonymous authentication + - page: docs-content://solutions/observability/apm/apm-server/apm-agent-authorization.md + title: APM agent authorization + - page: docs-content://solutions/observability/apm/apm-server/apm-agent-central-configuration.md + title: Configure APM Agent Central Configuration + - page: docs-content://solutions/observability/apm/apm-server/configure-apm-instrumentation.md + title: Instrumentation + - page: docs-content://solutions/observability/apm/apm-server/configure-kibana-endpoint.md + title: Kibana endpoint + - page: docs-content://solutions/observability/apm/apm-server/configure-logging.md + title: Logging + - group: Output + page: docs-content://solutions/observability/apm/apm-server/configure-output.md + children: + - page: docs-content://solutions/observability/apm/apm-server/configure-output-for-elasticsearch-service-on-elastic-cloud.md + title: Elastic Cloud Hosted + - page: docs-content://solutions/observability/apm/apm-server/configure-elasticsearch-output.md + title: Elasticsearch + - page: docs-content://solutions/observability/apm/apm-server/configure-logstash-output.md + title: "Logstash" + - page: docs-content://solutions/observability/apm/apm-server/configure-kafka-output.md + title: Kafka + - page: docs-content://solutions/observability/apm/apm-server/configure-redis-output.md + title: Redis + - page: docs-content://solutions/observability/apm/apm-server/configure-console-output.md + title: Console + - page: docs-content://solutions/observability/apm/apm-server/configure-project-paths.md + title: Project paths + - page: docs-content://solutions/observability/apm/apm-server/configure-real-user-monitoring-rum.md + title: Real User Monitoring (RUM) + - group: SSL/TLS settings + page: docs-content://solutions/observability/apm/apm-server/ssl-tls-settings.md + children: + - page: docs-content://solutions/observability/apm/apm-server/ssl-tls-output-settings.md + title: SSL/TLS output settings + - page: docs-content://solutions/observability/apm/apm-server/ssl-tls-input-settings.md + title: SSL/TLS input settings + - page: docs-content://solutions/observability/apm/apm-server/tail-based-sampling.md + title: Tail-based sampling + - page: docs-content://solutions/observability/apm/apm-server/use-environment-variables-in-configuration.md + title: Use environment variables in the configuration + - group: Advanced setup + page: docs-content://solutions/observability/apm/apm-server/advanced-setup.md + children: + - page: docs-content://solutions/observability/apm/apm-server/installation-layout.md + title: Installation layout + - page: docs-content://solutions/observability/apm/apm-server/secrets-keystore-for-secure-settings.md + title: Secrets keystore + - page: docs-content://solutions/observability/apm/apm-server/command-reference.md + title: Command reference + - page: docs-content://solutions/observability/apm/apm-server/tune-data-ingestion.md + title: Tune data ingestion + - page: docs-content://solutions/observability/apm/apm-server/high-availability.md + title: High Availability + - page: docs-content://solutions/observability/apm/apm-server/systemd.md + title: APM Server and systemd + - group: Monitor + page: docs-content://solutions/observability/apm/apm-server/monitor.md + children: + - page: docs-content://solutions/observability/apm/apm-server/monitor-fleet-managed.md + title: Fleet-managed + - group: APM Server binary + page: docs-content://solutions/observability/apm/apm-server/monitor-binary.md + children: + - page: docs-content://solutions/observability/apm/apm-server/use-internal-collection-to-send-monitoring-data.md + title: Use internal collection + - page: docs-content://solutions/observability/apm/apm-server/use-metricbeat-to-send-monitoring-data.md + title: "Use Metricbeat collection" + - page: docs-content://solutions/observability/apm/apm-server/use-select-metrics-emitted-directly-to-monitoring-cluster.md + title: Use local collection + - group: APM APIs + page: docs-content://solutions/observability/apm/apis.md + children: + - page: docs-content://solutions/observability/apm/apm-ui-api.md + title: APM UI API + - group: "APM Server API" + page: docs-content://solutions/observability/apm/apm-server/api.md + children: + - page: docs-content://solutions/observability/apm/apm-server/information-api.md + title: APM Server information API + - page: docs-content://solutions/observability/apm/elastic-apm-events-intake-api.md + title: Elastic APM events intake API + - page: docs-content://solutions/observability/apm/elastic-apm-agent-configuration-api.md + title: Elastic APM agent configuration API + - page: docs-content://solutions/observability/apm/opentelemetry-intake-api.md + title: OpenTelemetry intake API + - page: docs-content://solutions/observability/apm/jaeger-event-intake.md + title: Jaeger event intake + - page: docs-content://solutions/observability/apm/managed-intake-service-event-api.md + title: Managed intake service event API + - group: Upgrade + page: docs-content://solutions/observability/apm/upgrade.md + children: + - page: docs-content://solutions/observability/apm/apm-agent-compatibility.md + title: APM agent compatibility + - group: "Upgrade to version 9.0 [apm-upgrading-to-9.0]" + page: docs-content://solutions/observability/apm/upgrade-to-version-9.md + children: + - page: docs-content://solutions/observability/apm/upgrade-self-installation-of-apm-server-standalone-to-9.md + title: Self-installation standalone + - page: docs-content://solutions/observability/apm/upgrade-self-installation-of-apm-integration-to-9.md + title: Self-installation APM integration + - page: docs-content://solutions/observability/apm/upgrade-elastic-cloud-apm-server-standalone-to-9.md + title: "Elastic Cloud standalone" + - page: docs-content://solutions/observability/apm/upgrade-elastic-cloud-with-apm-integration-to-9.md + title: "Elastic Cloud APM integration" + - group: Switch to the Elastic APM integration + page: docs-content://solutions/observability/apm/switch-to-elastic-apm-integration.md + children: + - page: docs-content://solutions/observability/apm/switch-self-installation-to-apm-integration.md + title: Switch a self-installation + - page: docs-content://solutions/observability/apm/switch-an-elastic-cloud-cluster-to-apm-integration.md + title: "Switch an Elastic Cloud cluster" + - group: Synthetic monitoring + page: docs-content://solutions/observability/synthetics/index.md + children: + - group: Get started + page: docs-content://solutions/observability/synthetics/get-started.md + children: + - page: docs-content://solutions/observability/synthetics/create-monitors-with-projects.md + title: Use a Synthetics project + - page: docs-content://solutions/observability/synthetics/create-monitors-ui.md + title: Use the Synthetics UI + - group: Scripting browser monitors + page: docs-content://solutions/observability/synthetics/scripting-browser-monitors.md + children: + - page: docs-content://solutions/observability/synthetics/write-synthetic-test.md + title: Write a synthetic test + - page: docs-content://solutions/observability/synthetics/configure-individual-browser-monitors.md + title: Configure individual monitors + - page: docs-content://solutions/observability/synthetics/use-synthetics-recorder.md + title: Use the Synthetics Recorder + - page: docs-content://solutions/observability/synthetics/configure-lightweight-monitors.md + title: Configure lightweight monitors + - page: docs-content://solutions/observability/synthetics/manage-monitors.md + title: Manage monitors + - page: docs-content://solutions/observability/synthetics/work-with-params-secrets.md + title: Work with params and secrets + - page: docs-content://solutions/observability/synthetics/analyze-data.md + title: Analyze monitor data + - page: docs-content://solutions/observability/synthetics/monitor-resources-on-private-networks.md + title: Monitor resources on private networks + - page: docs-content://solutions/observability/synthetics/cli.md + title: Use the CLI + - page: docs-content://solutions/observability/synthetics/configure-projects.md + title: Configure a Synthetics project + - page: docs-content://solutions/observability/synthetics/mfa-for-browser-monitors.md + title: Multi-factor Authentication + - page: docs-content://solutions/observability/synthetics/configure-settings.md + title: Configure Synthetics settings + - group: Grant users access to secured resources + page: docs-content://solutions/observability/synthetics/grant-access-to-secured-resources.md + children: + - page: docs-content://solutions/observability/synthetics/setup-role.md + title: Setup role + - page: docs-content://solutions/observability/synthetics/writer-role.md + title: Writer role + - page: docs-content://solutions/observability/synthetics/reader-role.md + title: Reader role + - page: docs-content://solutions/observability/synthetics/manage-data-retention.md + title: Manage data retention + - page: docs-content://solutions/observability/synthetics/network-security.md + title: Use Synthetics with network security + - page: docs-content://solutions/observability/synthetics/migrate-from-elastic-synthetics-integration.md + title: Migrate from the Elastic Synthetics integration + - page: docs-content://solutions/observability/synthetics/scale-architect-synthetics-deployment.md + title: Scale and architect a deployment + - page: docs-content://solutions/observability/synthetics/support-matrix.md + title: Synthetics support matrix + - page: docs-content://solutions/observability/synthetics/encryption-security.md + title: Synthetics Encryption and Security + - group: Real user monitoring + page: docs-content://solutions/observability/applications/user-experience.md + children: + - page: docs-content://solutions/observability/applications/otel-rum.md + title: OpenTelemetry for Real User Monitoring (RUM) + - page: docs-content://solutions/observability/applications/llm-observability.md + title: LLM and agentic AI observability + - group: Uptime monitoring (deprecated) + page: docs-content://solutions/observability/uptime/index.md + children: + - page: docs-content://solutions/observability/uptime/get-started.md + title: Get started + - group: Analyze + page: docs-content://solutions/observability/uptime/analyze.md + children: + - page: docs-content://solutions/observability/uptime/view-monitor-status.md + title: View monitor status + - page: docs-content://solutions/observability/uptime/analyze-monitors.md + title: Analyze monitors + - page: docs-content://solutions/observability/uptime/inspect-duration-anomalies.md + title: Inspect uptime duration anomalies + - page: docs-content://solutions/observability/uptime/configure-settings.md + title: Configure settings + - page: docs-content://solutions/observability/otlp-visualize.md + title: Visualize OpenTelemetry data + - page: docs-content://solutions/observability/cicd.md + title: CI/CD + - group: Cloud + page: docs-content://solutions/observability/cloud.md + children: + - group: AWS + page: docs-content://solutions/observability/cloud/amazon-web-services-aws-monitoring.md + children: + - page: docs-content://solutions/observability/cloud/ingestion-options.md + title: Ingestion options + - group: "Monitor AWS with Elastic Agent" + page: docs-content://solutions/observability/cloud/monitor-amazon-web-services-aws-with-elastic-agent.md + children: + - page: docs-content://solutions/observability/cloud/monitor-amazon-cloud-compute-ec2.md + title: EC2 + - page: docs-content://solutions/observability/cloud/monitor-amazon-kinesis-data-streams.md + title: Kinesis data streams + - page: docs-content://solutions/observability/cloud/monitor-amazon-simple-storage-service-s3.md + title: S3 + - page: docs-content://solutions/observability/cloud/monitor-amazon-simple-queue-service-sqs.md + title: SQS + - page: docs-content://solutions/observability/cloud/monitor-amazon-web-services-aws-with-beats.md + title: "Monitor AWS with Beats" + - group: "Monitor AWS with Amazon Data Firehose" + page: docs-content://solutions/observability/cloud/monitor-amazon-web-services-aws-with-amazon-data-firehose.md + children: + - page: docs-content://solutions/observability/cloud/monitor-virtual-private-cloud-vpc-flow-logs.md + title: VPC Flow Logs + - page: docs-content://solutions/observability/cloud/monitor-cloudtrail-logs.md + title: CloudTrail logs + - page: docs-content://solutions/observability/cloud/monitor-aws-network-firewall-logs.md + title: Network Firewall logs + - page: docs-content://solutions/observability/cloud/monitor-web-application-firewall-waf-logs.md + title: WAF logs + - page: docs-content://solutions/observability/cloud/monitor-cloudwatch-logs.md + title: CloudWatch logs + - page: docs-content://solutions/observability/cloud/monitor-amazon-web-services-aws-with-elastic-serverless-forwarder.md + title: "Monitor AWS with Elastic Serverless Forwarder" + - group: Azure + page: docs-content://solutions/observability/cloud/azure-monitoring.md + children: + - page: docs-content://solutions/observability/cloud/monitor-microsoft-azure-with-elastic-agent.md + title: Monitor Microsoft Azure with Elastic Agent + - page: docs-content://solutions/observability/cloud/monitor-microsoft-azure-with-beats.md + title: Monitor Microsoft Azure with Beats + - page: docs-content://solutions/observability/cloud/multi-tenant-data-ingestion-with-beats.md + title: Ingest multi-tenant Azure Event Hub logs with Filebeat + - page: docs-content://solutions/observability/cloud/monitor-microsoft-azure-with-azure-native-isv-service.md + title: Monitor Microsoft Azure with the Azure Native ISV Service + - page: docs-content://solutions/observability/cloud/monitor-microsoft-azure-openai.md + title: Monitor Microsoft Azure OpenAI + - group: GCP + page: docs-content://solutions/observability/cloud/monitor-google-cloud-platform-gcp.md + children: + - page: docs-content://solutions/observability/cloud/gcp-dataflow-templates.md + title: GCP Dataflow templates + - group: Infrastructure and hosts + page: docs-content://solutions/observability/infra-and-hosts.md + children: + - group: Analyze infrastructure and host metrics + page: docs-content://solutions/observability/infra-and-hosts/analyze-infrastructure-host-metrics.md + children: + - page: docs-content://solutions/observability/infra-and-hosts/get-started-with-system-metrics.md + title: Get started with system metrics + - page: docs-content://solutions/observability/infra-and-hosts/view-infrastructure-metrics-by-resource-type.md + title: View infrastructure metrics by resource type + - page: docs-content://solutions/observability/infra-and-hosts/discover-metrics.md + title: Explore metrics data with Discover in Kibana + - page: docs-content://solutions/observability/infra-and-hosts/explore-infrastructure-metrics-over-time.md + title: Explore infrastructure metrics over time + - page: docs-content://solutions/observability/infra-and-hosts/analyze-compare-hosts.md + title: Analyze and compare hosts + - page: docs-content://solutions/observability/infra-and-hosts/detect-metric-anomalies.md + title: Detect metric anomalies + - page: docs-content://solutions/observability/infra-and-hosts/configure-settings.md + title: Configure settings + - group: Universal Profiling + page: docs-content://solutions/observability/infra-and-hosts/universal-profiling.md + children: + - page: docs-content://solutions/observability/infra-and-hosts/get-started-with-universal-profiling.md + title: Get started + - group: Manage data storage + page: docs-content://solutions/observability/infra-and-hosts/manage-data-storage.md + children: + - page: docs-content://solutions/observability/infra-and-hosts/universal-profiling-index-life-cycle-management.md + title: Index lifecycle management + - page: docs-content://solutions/observability/infra-and-hosts/configure-probabilistic-profiling.md + title: Configure probabilistic profiling + - group: Advanced configuration + page: docs-content://solutions/observability/infra-and-hosts/advanced-configuration.md + children: + - page: docs-content://solutions/observability/infra-and-hosts/tag-data-for-querying.md + title: Tag data for querying + - page: docs-content://solutions/observability/infra-and-hosts/add-symbols-for-native-frames.md + title: Add symbols for native frames + - page: docs-content://solutions/observability/infra-and-hosts/use-proxy-with-universal-profiling-agent.md + title: Use a proxy + - page: docs-content://solutions/observability/infra-and-hosts/override-kernel-version-check.md + title: Override kernel version check + - page: docs-content://solutions/observability/infra-and-hosts/environment-variables-to-configure-universal-profiling-agent.md + title: Environment variables to configure the Universal Profiling Agent + - page: docs-content://solutions/observability/infra-and-hosts/configuration-file-of-universal-profiling-agent.md + title: Configuration file of the Universal Profiling Agent + - page: docs-content://solutions/observability/infra-and-hosts/upgrade-universal-profiling.md + title: Upgrade + - page: docs-content://solutions/observability/infra-and-hosts/run-universal-profiling-on-self-hosted-elastic-stack.md + title: Self-hosted infrastructure + - group: Install the backend + page: docs-content://solutions/observability/infra-and-hosts/install-backend.md + children: + - page: docs-content://solutions/observability/infra-and-hosts/step-1-update-stack.md + title: "Step 1: Update the stack" + - page: docs-content://solutions/observability/infra-and-hosts/step-2-enable-universal-profiling-in-kibana.md + title: "Step 2: Enable Universal Profiling in Kibana" + - page: docs-content://solutions/observability/infra-and-hosts/step-3-set-up-universal-profiling-in-kibana.md + title: "Step 3: Set up Universal Profiling in Kibana" + - page: docs-content://solutions/observability/infra-and-hosts/step-4-run-backend-applications.md + title: "Step 4: Run the backend applications" + - page: docs-content://solutions/observability/infra-and-hosts/step-5-next-steps.md + title: "Step 5: Next steps" + - page: docs-content://solutions/observability/infra-and-hosts/operate-universal-profiling-backend.md + title: Operate the backend + - page: docs-content://solutions/observability/infra-and-hosts/tutorial-observe-kubernetes-deployments.md + title: "Tutorial: Observe your Kubernetes deployments" + - group: "Tutorial: Observe your nginx instances" + page: docs-content://solutions/observability/infra-and-hosts/tutorial-observe-nginx-instances.md + children: + - page: docs-content://solutions/observability/infra-and-hosts/understanding-no-results-found-message.md + title: Understanding "no results found" message + - page: docs-content://solutions/observability/infra-and-hosts/collect-nginx-data-otel-integration-fleet-managed.md + title: Collect NGINX data with OpenTelemetry integrations (Fleet-managed) + - page: docs-content://solutions/observability/infra-and-hosts/collect-nginx-data-otel-integration-standalone.md + title: Collect NGINX data with OpenTelemetry integrations (standalone) + - group: Logs + page: docs-content://solutions/observability/logs.md + children: + - page: docs-content://solutions/observability/logs/get-started-with-system-logs.md + title: Get started with system logs + - page: docs-content://solutions/observability/logs/stream-any-log-file.md + title: Send any log file using Elastic Agent + - page: docs-content://solutions/observability/logs/stream-any-log-file-using-edot-collector.md + title: Send any log file using OTel Collector + - group: Send application log data + page: docs-content://solutions/observability/logs/stream-application-logs.md + children: + - page: docs-content://solutions/observability/logs/plaintext-application-logs.md + title: Plaintext application logs + - page: docs-content://solutions/observability/logs/ecs-formatted-application-logs.md + title: ECS formatted application logs + - page: docs-content://solutions/observability/logs/apm-agent-log-sending.md + title: APM agent log sending + - page: docs-content://solutions/observability/logs/parse-route-logs.md + title: Parse and route logs using ingest pipelines + - page: docs-content://solutions/observability/logs/filter-aggregate-logs.md + title: Filter and aggregate logs + - group: Explore logs + page: docs-content://solutions/observability/logs/explore-logs.md + children: + - page: docs-content://solutions/observability/logs/discover-logs.md + title: Explore logs in Discover + - page: docs-content://solutions/observability/logs/categorize-log-entries.md + title: Categorize log entries + - page: docs-content://solutions/observability/logs/inspect-log-anomalies.md + title: Inspect log anomalies + - page: docs-content://solutions/observability/logs/run-pattern-analysis-on-log-data.md + title: Run a pattern analysis on log data + - page: docs-content://solutions/observability/logs/log-data-sources.md + title: Configure log data sources + - page: docs-content://solutions/observability/logs/logs-data-retention.md + title: Configure log data retention + - page: docs-content://solutions/observability/logs/add-service-name-to-logs.md + title: Add a service name to logs + - group: Logs index template reference + page: docs-content://solutions/observability/logs/logs-index-template-reference.md + children: + - page: docs-content://solutions/observability/logs/logs-index-template-defaults.md + title: Default `logs` index template + - page: docs-content://solutions/observability/streams/streams.md + title: Streams + - page: docs-content://solutions/observability/streams/management/retention.md + title: Manage data retention + - group: Process documents + page: docs-content://solutions/observability/streams/management/extract.md + children: + - page: docs-content://solutions/observability/streams/management/extract/drop.md + title: Drop document processor + - page: docs-content://solutions/observability/streams/management/extract/remove.md + title: Remove processor + - page: docs-content://solutions/observability/streams/management/extract/date.md + title: Date processor + - page: docs-content://solutions/observability/streams/management/extract/convert.md + title: Convert processor + - page: docs-content://solutions/observability/streams/management/extract/replace.md + title: Replace processor + - page: docs-content://solutions/observability/streams/management/extract/dissect.md + title: Dissect processor + - page: docs-content://solutions/observability/streams/management/extract/grok.md + title: Grok processor + - page: docs-content://solutions/observability/streams/management/extract/set.md + title: Set processor + - page: docs-content://solutions/observability/streams/management/extract/math.md + title: Math processor + - page: docs-content://solutions/observability/streams/management/extract/rename.md + title: Rename processor + - page: docs-content://solutions/observability/streams/management/extract/append.md + title: Append processor + - page: docs-content://solutions/observability/streams/management/extract/concat.md + title: Concat processor + - page: docs-content://solutions/observability/streams/management/extract/join.md + title: Join processor + - page: docs-content://solutions/observability/streams/management/extract/lowercase.md + title: Lowercase processor + - page: docs-content://solutions/observability/streams/management/extract/uppercase.md + title: Uppercase processor + - page: docs-content://solutions/observability/streams/management/extract/trim.md + title: Trim processor + - page: docs-content://solutions/observability/streams/management/extract/redact.md + title: Redact processor + - page: docs-content://solutions/observability/streams/management/extract/network-direction.md + title: Network direction processor + - page: docs-content://solutions/observability/streams/management/extract/manual-pipeline-configuration.md + title: Manual pipeline configuration + - page: docs-content://solutions/observability/streams/management/streamlang.md + title: Streamlang + - page: docs-content://solutions/observability/streams/management/partitioning.md + title: Partition data into child streams + - page: docs-content://solutions/observability/streams/management/schema.md + title: Map fields + - page: docs-content://solutions/observability/streams/management/data-quality.md + title: Manage data quality + - page: docs-content://solutions/observability/streams/management/advanced.md + title: Configure advanced settings + - page: docs-content://solutions/observability/streams/wired-streams.md + title: Wired streams + - group: Incident management + page: docs-content://solutions/observability/incident-management.md + children: + - group: Alerting + page: docs-content://solutions/observability/incident-management/alerting.md + children: + - group: Create and manage rules + page: docs-content://solutions/observability/incident-management/create-manage-rules.md + children: + - page: docs-content://solutions/observability/incident-management/create-an-anomaly-detection-rule.md + title: Anomaly detection + - page: docs-content://solutions/observability/incident-management/create-an-apm-anomaly-rule.md + title: APM anomaly + - page: docs-content://solutions/observability/incident-management/create-custom-threshold-rule.md + title: Custom threshold + - page: docs-content://solutions/observability/incident-management/create-a-degraded-docs-rule.md + title: Degraded docs + - page: docs-content://solutions/observability/incident-management/create-an-elasticsearch-query-rule.md + title: Elasticsearch query + - page: docs-content://solutions/observability/incident-management/create-an-error-count-threshold-rule.md + title: Error count threshold + - page: docs-content://solutions/observability/incident-management/create-failed-transaction-rate-threshold-rule.md + title: Failed transaction rate threshold + - page: docs-content://solutions/observability/incident-management/create-a-failed-docs-rule.md + title: Failed docs + - page: docs-content://solutions/observability/incident-management/create-an-inventory-rule.md + title: Inventory + - page: docs-content://solutions/observability/incident-management/create-latency-threshold-rule.md + title: Latency threshold + - page: docs-content://solutions/observability/incident-management/create-log-threshold-rule.md + title: Log threshold + - page: docs-content://solutions/observability/incident-management/create-metric-threshold-rule.md + title: Metric threshold + - page: docs-content://solutions/observability/incident-management/create-monitor-status-rule.md + title: Monitor Status + - page: docs-content://solutions/observability/incident-management/create-tls-certificate-rule.md + title: TLS certificate + - page: docs-content://solutions/observability/incident-management/create-an-uptime-duration-anomaly-rule.md + title: Uptime duration anomaly + - page: docs-content://solutions/observability/incident-management/create-an-slo-burn-rate-rule.md + title: SLO burn rate + - group: Aggregation options + page: docs-content://solutions/observability/incident-management/aggregation-options.md + children: + - page: docs-content://solutions/observability/incident-management/rate-aggregation.md + title: Rate aggregation + - group: View and manage alerts + page: docs-content://solutions/observability/incident-management/view-alerts.md + children: + - page: docs-content://solutions/observability/incident-management/triage-slo-burn-rate-breaches.md + title: SLO burn rate breaches + - page: docs-content://solutions/observability/incident-management/triage-threshold-breaches.md + title: Threshold breaches + - page: docs-content://solutions/observability/incident-management/observability-cases.md + title: Cases + - group: Service-level objectives (SLOs) + page: docs-content://solutions/observability/incident-management/service-level-objectives-slos.md + children: + - page: docs-content://solutions/observability/incident-management/configure-service-level-objective-slo-access.md + title: Configure SLO access + - page: docs-content://solutions/observability/incident-management/create-an-slo.md + title: Create an SLO + - page: docs-content://solutions/observability/incident-management/slo-management.md + title: View and manage SLOs + - page: docs-content://solutions/observability/incident-management/configure-slo-settings.md + title: Configure SLOs settings + - page: docs-content://solutions/observability/data-set-quality-monitoring.md + title: Data set quality + - group: AI for Observability + page: docs-content://solutions/observability/ai/observability-ai.md + children: + - page: docs-content://solutions/observability/ai/observability-ai-assistant.md + title: AI Assistant + - page: docs-content://solutions/observability/ai/agent-builder-observability.md + title: Agent Builder for Observability + - page: docs-content://solutions/observability/ai/llm-performance-matrix.md + title: Large language model performance matrix + - page: docs-content://solutions/observability/observability-serverless-feature-tiers.md + title: Serverless feature tiers + - page: docs-content://solutions/observability/apis.md + title: APIs + - group: Security solution + page: docs-content://solutions/security.md + children: + - group: Get started + page: docs-content://solutions/security/get-started.md + children: + - group: Elastic Security quickstarts + page: docs-content://solutions/security/get-started/quickstarts.md + children: + - page: docs-content://solutions/security/get-started/get-started-detect-with-siem.md + title: Detect and respond to threats with SIEM + - page: docs-content://solutions/security/get-started/get-started-endpoint-security.md + title: Protect your hosts with endpoint security + - page: docs-content://solutions/security/get-started/get-started-cloud-security.md + title: Secure your cloud assets with cloud security posture management + - page: docs-content://solutions/security/get-started/elastic-security-requirements.md + title: Elastic Security requirements + - page: docs-content://solutions/security/get-started/elastic-security-ui.md + title: Elastic Security UI + - group: Ingest data to Elastic Security + page: docs-content://solutions/security/get-started/ingest-data-to-elastic-security.md + children: + - page: docs-content://solutions/security/get-started/enable-threat-intelligence-integrations.md + title: Enable threat intelligence integrations + - page: docs-content://solutions/security/get-started/automatic-migration.md + title: Automatic migration + - page: docs-content://solutions/security/get-started/automatic-import.md + title: Automatic import + - page: docs-content://solutions/security/get-started/content-connectors.md + title: Content connectors + - group: Spaces and Elastic Security + page: docs-content://solutions/security/get-started/spaces-elastic-security.md + children: + - page: docs-content://solutions/security/get-started/spaces-defend-faq.md + title: "Spaces and Elastic Defend FAQ" + - page: docs-content://solutions/security/get-started/data-views-elastic-security.md + title: "Data views and Elastic Security" + - page: docs-content://solutions/security/get-started/create-runtime-fields-in-elastic-security.md + title: Create runtime fields in Elastic Security + - page: docs-content://solutions/security/get-started/configure-advanced-settings.md + title: Configure advanced settings + - group: "ES|QL for security" + page: docs-content://solutions/security/esql-for-security.md + children: + - page: docs-content://solutions/security/esql-for-security/esql-threat-hunting-tutorial.md + title: "Tutorial: Threat hunting with ES|QL" + - group: AI for security + page: docs-content://solutions/security/ai.md + children: + - group: Elastic AI SOC Engine + page: docs-content://solutions/security/ai/ease/ease-intro.md + children: + - page: docs-content://solutions/security/ai/ease/ease-alerts.md + title: Triage alerts + - page: docs-content://solutions/security/ai/ease/ease-upgrade.md + title: Upgrade from EASE to Elastic Security + - group: AI Assistant for Security + page: docs-content://solutions/security/ai/ai-assistant.md + children: + - page: docs-content://solutions/security/ai/ai-assistant-knowledge-base.md + title: AI Assistant Knowledge Base + - page: docs-content://solutions/security/ai/usecase-knowledge-base-walkthrough.md + title: Use AI Assistant's Knowledge Base to improve response quality + - page: docs-content://solutions/security/ai/agent-builder/agent-builder.md + title: Agent Builder for Elastic Security + - page: docs-content://solutions/security/ai/attack-discovery.md + title: Attack Discovery + - page: docs-content://solutions/security/ai/large-language-model-performance-matrix.md + title: Large language model performance matrix + - group: AI use cases + page: docs-content://solutions/security/ai/use-cases.md + children: + - page: docs-content://solutions/security/ai/triage-alerts.md + title: Triage alerts + - page: docs-content://solutions/security/ai/identify-investigate-document-threats.md + title: Identify, investigate, and document threats + - page: docs-content://solutions/security/ai/generate-customize-learn-about-esorql-queries.md + title: "Generate, customize, and learn about ES|QL queries" + - page: docs-content://solutions/security/ai/ease/ease-value-report.md + title: Value report + - group: Detections and alerts + page: docs-content://solutions/security/detect-and-alert.md + children: + - group: Before you begin + page: docs-content://solutions/security/detect-and-alert/before-you-begin.md + children: + - page: docs-content://solutions/security/detect-and-alert/turn-on-detections.md + title: Turn on detections + - page: docs-content://solutions/security/detect-and-alert/detections-privileges.md + title: Detections privileges + - page: docs-content://solutions/security/detect-and-alert/detection-rule-concepts.md + title: Detection rule concepts + - group: Advanced data source configuration + page: docs-content://solutions/security/detect-and-alert/advanced-data-source-configuration.md + children: + - page: docs-content://solutions/security/detect-and-alert/cross-cluster-search-detection-rules.md + title: Cross-cluster search and detection rules + - page: docs-content://solutions/security/detect-and-alert/using-logsdb-index-mode-with-elastic-security.md + title: Using logsdb index mode with Elastic Security + - page: docs-content://solutions/security/detect-and-alert/mitre-attack-coverage.md + title: "MITRE ATT&CK coverage" + - group: Prebuilt rules + page: docs-content://solutions/security/detect-and-alert/prebuilt-rules.md + children: + - page: docs-content://solutions/security/detect-and-alert/prebuilt-rule-components.md + title: Prebuilt rule components + - page: docs-content://solutions/security/detect-and-alert/install-prebuilt-rules.md + title: Install prebuilt rules + - page: docs-content://solutions/security/detect-and-alert/update-prebuilt-rules.md + title: Update prebuilt rules + - page: docs-content://solutions/security/detect-and-alert/prebuilt-rules-airgapped.md + title: Prebuilt rules in air-gapped environments + - page: docs-content://solutions/security/detect-and-alert/customize-prebuilt-rules.md + title: Customize prebuilt rules + - group: Author rules + page: docs-content://solutions/security/detect-and-alert/author-rules.md + children: + - group: Choose the right rule type + page: docs-content://solutions/security/detect-and-alert/choose-the-right-rule-type.md + children: + - page: docs-content://solutions/security/detect-and-alert/about-building-block-rules.md + title: About building block rules + - group: Rule type guides + page: docs-content://solutions/security/detect-and-alert/rule-types.md + children: + - page: docs-content://solutions/security/detect-and-alert/esql.md + title: "ES|QL rules" + - page: docs-content://solutions/security/detect-and-alert/custom-query.md + title: Custom query rules + - page: docs-content://solutions/security/detect-and-alert/eql.md + title: Event correlation (EQL) rules + - page: docs-content://solutions/security/detect-and-alert/indicator-match.md + title: Indicator match rules + - page: docs-content://solutions/security/detect-and-alert/threshold.md + title: Threshold rules + - page: docs-content://solutions/security/detect-and-alert/machine-learning.md + title: "Machine learning rules" + - page: docs-content://solutions/security/detect-and-alert/new-terms.md + title: New terms rules + - page: docs-content://solutions/security/detect-and-alert/using-the-rule-ui.md + title: Using the UI + - page: docs-content://solutions/security/detect-and-alert/using-the-api.md + title: Using the API + - page: docs-content://solutions/security/detect-and-alert/common-rule-settings.md + title: Common rule settings + - page: docs-content://solutions/security/detect-and-alert/set-rule-data-sources.md + title: Set rule data sources + - page: docs-content://solutions/security/detect-and-alert/write-investigation-guides.md + title: Write investigation guides + - page: docs-content://solutions/security/detect-and-alert/validate-and-test-rules.md + title: Validate and test rules + - page: docs-content://solutions/security/detect-and-alert/manage-detection-rules.md + title: Manage detection rules + - group: Monitor rule executions + page: docs-content://solutions/security/detect-and-alert/monitor-rule-executions.md + children: + - page: docs-content://solutions/security/detect-and-alert/fill-rule-gaps.md + title: Fill rule execution gaps + - group: Reduce noise and false positives + page: docs-content://solutions/security/detect-and-alert/reduce-noise-and-false-positives.md + children: + - page: docs-content://solutions/security/detect-and-alert/tune-detection-rules.md + title: Tune detection rules + - group: Rule exceptions + page: docs-content://solutions/security/detect-and-alert/rule-exceptions.md + children: + - page: docs-content://solutions/security/detect-and-alert/create-manage-value-lists.md + title: Create and manage value lists + - page: docs-content://solutions/security/detect-and-alert/add-manage-exceptions.md + title: Add and manage exceptions + - page: docs-content://solutions/security/detect-and-alert/create-manage-shared-exception-lists.md + title: Create and manage shared exception lists + - page: docs-content://solutions/security/detect-and-alert/alert-suppression.md + title: Suppress detection alerts + - group: Manage detection alerts + page: docs-content://solutions/security/detect-and-alert/manage-detection-alerts.md + children: + - page: docs-content://solutions/security/detect-and-alert/visualize-detection-alerts.md + title: Visualize detection alerts + - page: docs-content://solutions/security/detect-and-alert/view-detection-alert-details.md + title: View detection alert details + - page: docs-content://solutions/security/detect-and-alert/query-alert-indices.md + title: Query alert indices + - group: "Configure endpoint protection with Elastic Defend" + page: docs-content://solutions/security/configure-elastic-defend.md + children: + - page: docs-content://solutions/security/configure-elastic-defend/elastic-defend-requirements.md + title: "Elastic Defend requirements" + - group: "Install Elastic Defend" + page: docs-content://solutions/security/configure-elastic-defend/install-elastic-defend.md + children: + - page: docs-content://solutions/security/configure-elastic-defend/enable-access-for-macos.md + title: Enable access on macOS + - page: docs-content://solutions/security/configure-elastic-defend/deploy-on-macos-with-mdm.md + title: Deploy on macOS with MDM + - page: docs-content://solutions/security/configure-elastic-defend/prevent-elastic-agent-uninstallation.md + title: Prevent Elastic Agent uninstallation + - page: docs-content://solutions/security/configure-elastic-defend/elastic-defend-feature-privileges.md + title: "Elastic Defend feature privileges" + - group: "Configure an integration policy for Elastic Defend" + page: docs-content://solutions/security/configure-elastic-defend/configure-an-integration-policy-for-elastic-defend.md + children: + - page: docs-content://solutions/security/configure-elastic-defend/configure-updates-for-protection-artifacts.md + title: Configure updates for protection artifacts + - page: docs-content://solutions/security/configure-elastic-defend/turn-off-diagnostic-data-for-elastic-defend.md + title: "Turn off diagnostic data for Elastic Defend" + - page: docs-content://solutions/security/configure-elastic-defend/configure-self-healing-rollback-for-windows-endpoints.md + title: Configure self-healing rollback for Windows endpoints + - page: docs-content://solutions/security/configure-elastic-defend/configure-linux-file-system-monitoring.md + title: Configure Linux file system monitoring + - page: docs-content://solutions/security/configure-elastic-defend/configure-data-volume-for-elastic-endpoint.md + title: Configure data volume + - page: docs-content://solutions/security/configure-elastic-defend/create-an-elastic-defend-policy-using-api.md + title: Create an Elastic Defend policy using API + - page: docs-content://solutions/security/configure-elastic-defend/configure-offline-endpoints-air-gapped-environments.md + title: Configure offline endpoints and air-gapped environments + - page: docs-content://solutions/security/configure-elastic-defend/uninstall-elastic-agent.md + title: Uninstall Elastic Agent + - group: "Manage Elastic Defend" + page: docs-content://solutions/security/manage-elastic-defend.md + children: + - page: docs-content://solutions/security/manage-elastic-defend/endpoints.md + title: Endpoints + - page: docs-content://solutions/security/manage-elastic-defend/policies.md + title: Policies + - page: docs-content://solutions/security/manage-elastic-defend/trusted-applications.md + title: Trusted applications + - page: docs-content://solutions/security/manage-elastic-defend/trusted-devices.md + title: Trusted devices + - page: docs-content://solutions/security/manage-elastic-defend/event-filters.md + title: Event filters + - page: docs-content://solutions/security/manage-elastic-defend/host-isolation-exceptions.md + title: Host isolation exceptions + - page: docs-content://solutions/security/manage-elastic-defend/blocklist.md + title: Blocklist + - page: docs-content://solutions/security/manage-elastic-defend/optimize-elastic-defend.md + title: "Optimize Elastic Defend" + - page: docs-content://solutions/security/manage-elastic-defend/event-capture-elastic-defend.md + title: "Event capture and Elastic Defend" + - page: docs-content://solutions/security/manage-elastic-defend/endpoint-protection-rules.md + title: Endpoint protection rules + - page: docs-content://solutions/security/manage-elastic-defend/automatic-troubleshooting.md + title: Automatic troubleshooting + - page: docs-content://solutions/security/manage-elastic-defend/allowlist-elastic-endpoint-in-third-party-antivirus-apps.md + title: "Allowlist Elastic Endpoint in third-party antivirus apps" + - page: docs-content://solutions/security/manage-elastic-defend/elastic-endpoint-self-protection-features.md + title: "Elastic Endpoint self-protection features" + - group: Endpoint response actions + page: docs-content://solutions/security/endpoint-response-actions.md + children: + - page: docs-content://solutions/security/endpoint-response-actions/automated-response-actions.md + title: Automated response actions + - page: docs-content://solutions/security/endpoint-response-actions/isolate-host.md + title: Isolate a host + - page: docs-content://solutions/security/endpoint-response-actions/response-actions-history.md + title: Response actions history + - page: docs-content://solutions/security/endpoint-response-actions/third-party-response-actions.md + title: Third-party response actions + - page: docs-content://solutions/security/endpoint-response-actions/configure-third-party-response-actions.md + title: Configure third-party response actions + - group: Cloud Security + page: docs-content://solutions/security/cloud.md + children: + - page: docs-content://solutions/security/cloud/security-posture-management-overview.md + title: Security posture management overview + - page: docs-content://solutions/security/cloud/enable-cloud-security-features.md + title: "Enable cloud security features in Serverless" + - group: Cloud security posture management + page: docs-content://solutions/security/cloud/cloud-security-posture-management.md + children: + - page: docs-content://solutions/security/cloud/get-started-with-cspm-for-aws.md + title: Get started with CSPM for AWS + - page: docs-content://solutions/security/cloud/get-started-with-cspm-for-gcp.md + title: Get started with CSPM for GCP + - page: docs-content://solutions/security/cloud/get-started-with-cspm-for-azure.md + title: Get started with CSPM for Azure + - page: docs-content://solutions/security/cloud/cspm-privilege-requirements.md + title: CSPM privilege requirements + - page: docs-content://solutions/security/cloud/findings-page.md + title: CSPM Findings + - page: docs-content://solutions/security/cloud/benchmarks.md + title: CSPM benchmarks + - page: docs-content://solutions/security/cloud/cspm-dashboard.md + title: Cloud Security Posture dashboard + - page: docs-content://solutions/security/cloud/cspm-frequently-asked-questions-faq.md + title: Frequently asked questions (FAQ) + - group: Kubernetes security posture management + page: docs-content://solutions/security/cloud/kubernetes-security-posture-management.md + children: + - page: docs-content://solutions/security/cloud/get-started-with-kspm.md + title: Get started with KSPM + - page: docs-content://solutions/security/cloud/findings-page-2.md + title: KSPM Findings + - page: docs-content://solutions/security/cloud/kspm-benchmarks.md + title: KSPM benchmarks + - page: docs-content://solutions/security/cloud/kspm-dashboard.md + title: Cloud Security Posture dashboard + - page: docs-content://solutions/security/cloud/kspm-frequently-asked-questions.md + title: Frequently asked questions (FAQ) + - group: Cloud Asset Discovery + page: docs-content://solutions/security/cloud/asset-disc.md + children: + - page: docs-content://solutions/security/cloud/asset-disc-aws.md + title: Set up Cloud Asset Discovery for AWS + - page: docs-content://solutions/security/cloud/asset-disc-gcp.md + title: Set up Cloud Asset Discovery for GCP + - page: docs-content://solutions/security/cloud/asset-disc-azure.md + title: Set up Cloud Asset Discovery for Azure + - group: Cloud native vulnerability management + page: docs-content://solutions/security/cloud/cloud-native-vulnerability-management.md + children: + - page: docs-content://solutions/security/cloud/get-started-with-cnvm.md + title: Get started with CNVM + - page: docs-content://solutions/security/cloud/cnvm-privilege-requirements.md + title: CNVM privilege requirements + - page: docs-content://solutions/security/cloud/findings-page-3.md + title: CNVM Findings + - page: docs-content://solutions/security/cloud/cnvm-dashboard.md + title: CNVM dashboard + - page: docs-content://solutions/security/cloud/cnvm-frequently-asked-questions-faq.md + title: Frequently asked questions (FAQ) + - group: Cloud workload protection for VMs + page: docs-content://solutions/security/cloud/cloud-workload-protection-for-vms.md + children: + - page: docs-content://solutions/security/cloud/capture-environment-variables.md + title: Capture environment variables + - group: Cloud workload protection for Kubernetes + page: docs-content://solutions/security/cloud/d4c/d4c-overview.md + children: + - page: docs-content://solutions/security/cloud/d4c/get-started-with-d4c.md + title: Get started with Defend for Containers for Kubernetes + - page: docs-content://solutions/security/cloud/d4c/d4c-policies.md + title: Container workload protection policies + - page: docs-content://solutions/security/cloud/d4c/kubernetes-dashboard.md + title: Kubernetes dashboard + - group: Ingest third-party security data + page: docs-content://solutions/security/integrations/ingest-third-party-security-data.md + children: + - page: docs-content://solutions/security/integrations/aws-config.md + title: AWS Config + - page: docs-content://solutions/security/integrations/aws-inspector.md + title: AWS Inspector + - page: docs-content://solutions/security/integrations/aws-sec-hub.md + title: AWS Security Hub + - page: docs-content://solutions/security/integrations/aws-security-hub-cspm.md + title: AWS Security Hub CSPM + - page: docs-content://solutions/security/integrations/cncf-falco.md + title: CNCF Falco + - page: docs-content://solutions/security/integrations/google-security-command-center.md + title: Google Security Command Center + - page: docs-content://solutions/security/integrations/microsoft-defender-for-cloud.md + title: Microsoft Defender for Cloud + - page: docs-content://solutions/security/integrations/microsoft-defender-for-endpoint.md + title: Microsoft Defender for Endpoint + - page: docs-content://solutions/security/integrations/microsoft-defender-xdr.md + title: Microsoft Defender XDR + - page: docs-content://solutions/security/integrations/prisma-cloud.md + title: Prisma Cloud + - page: docs-content://solutions/security/integrations/qualys.md + title: Qualys VMDR + - page: docs-content://solutions/security/integrations/rapid7.md + title: Rapid7 InsightVM + - page: docs-content://solutions/security/integrations/tenablevm.md + title: Tenable VM + - page: docs-content://solutions/security/integrations/wiz.md + title: Wiz + - group: Investigation tools + page: docs-content://solutions/security/investigate.md + children: + - group: Timeline + page: docs-content://solutions/security/investigate/timeline.md + children: + - page: docs-content://solutions/security/investigate/timeline-templates.md + title: Timeline templates + - page: docs-content://solutions/security/investigate/visual-event-analyzer.md + title: Visual event analyzer + - page: docs-content://solutions/security/investigate/session-view.md + title: Session View + - group: Osquery + page: docs-content://solutions/security/investigate/osquery.md + children: + - page: docs-content://solutions/security/investigate/manage-integration.md + title: Osquery manager integration + - page: docs-content://solutions/security/investigate/osquery-faq.md + title: Osquery FAQ + - page: docs-content://solutions/security/investigate/add-osquery-response-actions.md + title: Add Osquery Response Actions + - page: docs-content://solutions/security/investigate/run-osquery-from-investigation-guides.md + title: Run Osquery from investigation guides + - page: docs-content://solutions/security/investigate/run-osquery-from-alerts.md + title: Run Osquery from alerts + - page: docs-content://solutions/security/investigate/examine-osquery-results.md + title: Examine Osquery results + - page: docs-content://solutions/security/investigate/use-placeholder-fields-in-osquery-queries.md + title: Use placeholder fields in Osquery queries + - page: docs-content://solutions/security/investigate/notes.md + title: Notes + - page: docs-content://solutions/security/investigate/indicators-of-compromise.md + title: Indicators of compromise + - page: docs-content://solutions/security/investigate/security-cases.md + title: Cases + - group: Dashboards + page: docs-content://solutions/security/dashboards.md + children: + - page: docs-content://solutions/security/dashboards/overview-dashboard.md + title: Overview dashboard + - page: docs-content://solutions/security/dashboards/detection-response-dashboard.md + title: "Detection & Response dashboard" + - page: docs-content://solutions/security/dashboards/cloud-security-posture-dashboard.md + title: Cloud Security Posture dashboard + - page: docs-content://solutions/security/dashboards/kubernetes-dashboard.md + title: Kubernetes dashboard + - page: docs-content://solutions/security/dashboards/entity-analytics-dashboard.md + title: Entity Analytics dashboard + - page: docs-content://solutions/security/dashboards/data-quality-dashboard.md + title: Data Quality dashboard + - page: docs-content://solutions/security/dashboards/cloud-native-vulnerability-management-dashboard.md + title: Cloud Native Vulnerability Management Dashboard + - page: docs-content://solutions/security/dashboards/detection-rule-monitoring-dashboard.md + title: Detection rule monitoring dashboard + - page: docs-content://solutions/security/dashboards/endpoint-detection-response-dashboard.md + title: Endpoint Detection and Response dashboard + - group: Entity analytics + page: docs-content://solutions/security/advanced-entity-analytics.md + children: + - page: docs-content://solutions/security/advanced-entity-analytics/overview.md + title: Entity analytics overview + - group: Entity risk scoring + page: docs-content://solutions/security/advanced-entity-analytics/entity-risk-scoring.md + children: + - page: docs-content://solutions/security/advanced-entity-analytics/entity-risk-scoring-requirements.md + title: Entity risk scoring requirements + - page: docs-content://solutions/security/advanced-entity-analytics/turn-on-risk-scoring-engine.md + title: Turn on the risk scoring engine + - page: docs-content://solutions/security/advanced-entity-analytics/view-entity-details.md + title: View entity details + - page: docs-content://solutions/security/advanced-entity-analytics/asset-criticality.md + title: Asset criticality + - page: docs-content://solutions/security/advanced-entity-analytics/entity-store.md + title: Entity store + - page: docs-content://solutions/security/advanced-entity-analytics/view-analyze-risk-score-data.md + title: View and analyze risk score data + - group: Advanced behavioral detections + page: docs-content://solutions/security/advanced-entity-analytics/advanced-behavioral-detections.md + children: + - page: docs-content://solutions/security/advanced-entity-analytics/machine-learning-job-rule-requirements.md + title: Machine learning job and rule requirements + - page: docs-content://solutions/security/advanced-entity-analytics/anomaly-detection.md + title: Anomaly detection + - page: docs-content://solutions/security/advanced-entity-analytics/optimizing-anomaly-results.md + title: Optimizing anomaly results + - page: docs-content://solutions/security/advanced-entity-analytics/behavioral-detection-use-cases.md + title: Behavioral detection use cases + - group: Privileged user monitoring + page: docs-content://solutions/security/advanced-entity-analytics/privileged-user-monitoring.md + children: + - page: docs-content://solutions/security/advanced-entity-analytics/privileged-user-monitoring-requirements.md + title: Privileged user monitoring requirements + - page: docs-content://solutions/security/advanced-entity-analytics/privileged-user-monitoring-setup.md + title: Set up privileged user monitoring + - page: docs-content://solutions/security/advanced-entity-analytics/monitor-privileged-user-activitites.md + title: Monitor privileged user activities + - group: Explore + page: docs-content://solutions/security/advanced-entity-analytics/explore.md + children: + - page: docs-content://solutions/security/advanced-entity-analytics/hosts-page.md + title: Hosts page + - group: Network page + page: docs-content://solutions/security/advanced-entity-analytics/network-page.md + children: + - page: docs-content://solutions/security/advanced-entity-analytics/configure-network-map-data.md + title: Configure network map data + - page: docs-content://solutions/security/advanced-entity-analytics/configure-dns-histogram.md + title: Configure the DNS histogram + - page: docs-content://solutions/security/advanced-entity-analytics/users-page.md + title: Users page + - page: docs-content://solutions/security/security-serverless-feature-tiers.md + title: Serverless feature tiers + - page: docs-content://solutions/security/apis.md + title: APIs + + - section: Products + url: /products/ + dropdown: true + children: + - title: Product hubs + - label: Stack products + children: + - page: docs-content://products/elasticsearch/v9.md + title: Elasticsearch + - page: docs-content://products/kibana/v9.md + title: Kibana + - title: Logstash (soon) + - title: Beats (soon) + - label: Deployment runbooks + children: + - title: Elastic Cloud Hosted (soon) + - title: Elastic Cloud Serverless (soon) + - title: Self-managed (soon) + + - section: APIs + url: https://www.elastic.co/docs/api + + - section: Reference + url: /reference/ + children: + - toc: docs-content://reference + - toc: elasticsearch://reference/elasticsearch + - toc: elasticsearch://reference/elasticsearch-plugins + - toc: elasticsearch://reference/community-contributed + - toc: kibana://reference + - group: Cloud + children: + - toc: cloud://reference + - toc: cloud-on-k8s://reference + - group: Elasticsearch clients + children: + - toc: elasticsearch-java://reference + - toc: elasticsearch-js://reference + - toc: elasticsearch-dsl-js://reference + - toc: elasticsearch-net://reference + - toc: elasticsearch-php://reference + - toc: elasticsearch-py://reference + - toc: elasticsearch-ruby://reference + - toc: elasticsearch-rs://reference + - toc: eland://reference + - toc: go-elasticsearch://reference + - toc: curator://reference + - group: APM + children: + - toc: apm-k8s-attacher://reference + - toc: apm-aws-lambda://reference + - toc: apm-agent-dotnet://reference + - toc: apm-agent-go://reference + - toc: apm-agent-java://reference + - toc: apm-agent-nodejs://reference + - toc: apm-agent-php://reference + - toc: apm-agent-python://reference + - toc: apm-agent-ruby://reference + - toc: apm-agent-rum-js://reference + - group: OpenTelemetry + children: + - toc: opentelemetry://reference + children: + - toc: opentelemetry://reference/motlp + - toc: opentelemetry://reference/edot-cloud-forwarder + children: + - toc: edot-cloud-forwarder-aws://reference/edot-cf-aws + - toc: edot-cloud-forwarder-azure://reference/edot-cf-azure + - toc: opentelemetry://reference/edot-cloud-forwarder/gcp + - toc: elastic-agent://reference/edot-collector + - toc: apm-agent-android://reference/edot-android + - toc: elastic-otel-dotnet://reference/edot-dotnet + - toc: apm-agent-ios://reference/edot-ios + - toc: elastic-otel-java://reference/edot-java + - toc: elastic-otel-node://reference/edot-node + - toc: elastic-otel-php://reference/edot-php + - toc: elastic-otel-python://reference/edot-python + - toc: elastic-otel-rum-js://reference/edot-browser + - group: ECS Logging + children: + - toc: ecs-logging://reference + children: + - toc: ecs-dotnet://reference + - toc: ecs-logging-go-logrus://reference + - toc: ecs-logging-go-zap://reference + - toc: ecs-logging-go-zerolog://reference + - toc: ecs-logging-java://reference + - toc: ecs-logging-nodejs://reference + - toc: ecs-logging-php://reference + - toc: ecs-logging-python://reference + - toc: ecs-logging-ruby://reference + - toc: beats://reference + - toc: logstash://reference + - toc: integration-docs://reference + - toc: elasticsearch-hadoop://reference + - toc: elastic-serverless-forwarder://reference + - toc: ecs://reference + - toc: search-ui://reference + - toc: elasticsearch://reference/aggregations + - toc: ecctl://reference + - toc: elasticsearch://reference/enrich-processor + - toc: elasticsearch://reference/query-languages + - toc: elasticsearch://reference/scripting-languages + - page: docs-content://explore-analyze/numeral-formatting.md + title: Numeral formatting + - toc: elasticsearch://reference/text-analysis + - island: Logstash plugins + toc: logstash-docs-md://lsr + - island: Logstash versioned plugins + toc: logstash-docs-md://vpr + + - section: Release notes + url: /release-notes/ + children: + - toc: docs-content://release-notes/intro + children: + # Elasticsearch + - toc: elasticsearch://release-notes + children: + # Elasticsearch Clients + - toc: elasticsearch-java://release-notes + - toc: elasticsearch-js://release-notes + - toc: elasticsearch-net://release-notes + - toc: elasticsearch-php://release-notes + - toc: elasticsearch-py://release-notes + - toc: elasticsearch-ruby://release-notes + # Elasticsearch Hadoop + - toc: elasticsearch-hadoop://release-notes + # Kibana + - toc: kibana://release-notes + # Elastic Agent + - toc: elastic-agent://release-notes + # Fleet Server + - toc: fleet-server://release-notes + # Logstash + - toc: logstash://release-notes + # Beats + - toc: beats://release-notes + # Serverless + - toc: docs-content://release-notes/elastic-cloud-serverless + # Cloud Hosted + - toc: cloud://release-notes/cloud-hosted + # Cloud Enterprise + - toc: cloud://release-notes/cloud-enterprise + # Cloud on Kubernetes + - toc: cloud-on-k8s://release-notes + # Observability + - toc: docs-content://release-notes/elastic-observability + children: + # EDOT SDKs + - toc: apm-agent-android://release-notes + - toc: edot-cloud-forwarder-aws://release-notes + - toc: apm-agent-ios://release-notes + - toc: elastic-otel-java://release-notes + - toc: elastic-otel-dotnet://release-notes + - toc: elastic-otel-node://release-notes + - toc: elastic-otel-python://release-notes + - toc: elastic-otel-php://release-notes + - toc: elastic-otel-rum-js://release-notes + # APM + - toc: apm-server://release-notes + # APM agents + - toc: apm-agent-dotnet://release-notes + - toc: apm-agent-go://release-notes + - toc: apm-agent-java://release-notes + - toc: apm-agent-nodejs://release-notes + - toc: apm-agent-php://release-notes + - toc: apm-agent-python://release-notes + - toc: apm-agent-ruby://release-notes + - toc: apm-agent-rum-js://release-notes + # APM AWS Lambda Extension + - toc: apm-aws-lambda://release-notes + # Security + - toc: docs-content://release-notes/elastic-security + # ECS + - toc: ecs://release-notes + # ECCTL + - toc: ecctl://release-notes + + - section: Troubleshoot + url: /troubleshoot/ + children: + - toc: docs-content://troubleshoot + + - section: Extension points + url: /extend/ + isolated: true + children: + - toc: docs-content://extend + children: + - toc: kibana://extend + - toc: logstash://extend + - toc: beats://extend + - toc: elasticsearch://extend + - toc: integrations://extend + + # Disabled pending a decision on whether to keep the contribute-docs + # content in the public site. Re-enable to resurface ~35 pages. + # - section: Contribute + # url: /contribute-docs/ + # isolated: true + # children: + # - toc: docs-content://contribute-docs + diff --git a/config/navigation.yml b/config/navigation.yml index 01d93faeec..3bf8dd5dda 100644 --- a/config/navigation.yml +++ b/config/navigation.yml @@ -17,6 +17,7 @@ toc: ############# # NARRATIVE # ############# + - toc: products - toc: get-started - toc: solutions - toc: manage-data diff --git a/config/products.yml b/config/products.yml index d96d39e83e..874f835c70 100644 --- a/config/products.yml +++ b/config/products.yml @@ -147,6 +147,7 @@ products: elasticsearch: display: 'Elasticsearch' versioning: 'stack' + hub: 'products/elasticsearch/v9' elasticsearch-client: display: 'Elasticsearch Client' versioning: 'stack' @@ -207,6 +208,7 @@ products: kibana: display: 'Kibana' versioning: 'stack' + hub: 'products/kibana/v9' logstash: display: 'Logstash' versioning: 'stack' diff --git a/config/whats-new.yml b/config/whats-new.yml new file mode 100644 index 0000000000..2cfb4f0105 --- /dev/null +++ b/config/whats-new.yml @@ -0,0 +1,72 @@ +### +# Centralized "What's new" feed used by the {whats-new} directive. +# +# Any page can render a product's recent highlights with: +# +# :::{whats-new} +# :product: kibana +# ::: +# +# The {whats-new} directive looks the product up in this file and renders the +# full panel (header + items). Authors only edit this file; the directive +# stays trivial in markdown. +# +# An optional `id` and `badge` per product control the section anchor and the +# small badge shown to the left of the title; default badge is "New". +### + +products: + + kibana: + title: What's new in Kibana + id: whats-new + badge: New + release-links: + - label: '9.4' + url: https://www.elastic.co/docs/release-notes/kibana + - label: Serverless + url: https://www.elastic.co/docs/release-notes/serverless + items: + - title: Dashboards and visualizations APIs + description: Programmatically create and manage dashboards and visualizations + link: https://www.elastic.co/docs/api/doc/kibana + meta: 9.4 preview + - title: AI agent skills + description: Teach AI coding agents to work with the Elastic stack + link: https://www.elastic.co/docs/explore-analyze/ai-features/agent-skills + meta: Mar 2026 + - title: Agent Builder + description: Build custom AI agents that reason over your data + link: https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder + meta: 9.3 GA + - title: Workflows + description: Automate tasks with Kibana actions, HTTP calls, and AI agents + link: https://www.elastic.co/docs/explore-analyze/workflows + meta: 9.3 preview + + elasticsearch: + title: What's new in Elasticsearch + id: whats-new + badge: New + release-links: + - label: '9.4' + url: https://www.elastic.co/docs/release-notes/elasticsearch + - label: Serverless + url: https://www.elastic.co/docs/release-notes/serverless + items: + - title: semantic_text field type + description: Simplify semantic search — no pipeline setup needed, just define the field and start querying + link: https://www.elastic.co/docs/solutions/search/semantic-search/semantic-search-semantic-text + meta: 9.4 GA + - title: ES|QL improvements + description: New functions, cross-cluster support, and dense vector operations directly in ES|QL queries + link: https://www.elastic.co/docs/reference/query-languages/esql/esql-getting-started + meta: '9.4' + - title: Inference API expansion + description: Connect to external AI models (OpenAI, Cohere, Anthropic, Hugging Face, Azure, Google) for embeddings and reranking + link: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put + meta: '9.4' + - title: Breaking changes in 9.x + description: Review removed settings, deprecated APIs, and behavioral changes before upgrading from 8.x + link: https://www.elastic.co/docs/release-notes/elasticsearch/breaking-changes + meta: Upgrade guide diff --git a/docs/_docset.yml b/docs/_docset.yml index d2bc1c11b5..39ea922004 100644 --- a/docs/_docset.yml +++ b/docs/_docset.yml @@ -128,6 +128,7 @@ toc: - file: applies-switch.md - file: automated_settings.md - file: buttons.md + - file: card-group.md - file: changelog.md - file: code.md - file: comments.md @@ -140,16 +141,21 @@ toc: - file: file_inclusion.md - file: footnotes.md - file: frontmatter.md + - file: hero.md + - file: hub-pages.md - file: icons.md + - file: intro.md - file: images.md - file: videos.md - file: kbd.md + - file: link-card.md - file: math.md - file: diagrams.md - file: lists.md - file: line_breaks.md - file: links.md - file: list-sub-pages.md + - file: on-this-page.md - file: page-card.md - file: passthrough.md - file: sidebars.md @@ -161,6 +167,7 @@ toc: - file: tabs.md - file: tagged_regions.md - file: titles.md + - file: whats-new.md - cli: cli-schema.json folder: cli children: @@ -193,6 +200,14 @@ toc: - folder: testing children: - file: index.md + - folder: products + children: + - folder: elasticsearch + children: + - file: v9.md + - folder: kibana + children: + - file: v9.md - file: kibana-settings-yaml-samples.md - file: security-settings.md - file: req.md diff --git a/docs/syntax/card-group.md b/docs/syntax/card-group.md new file mode 100644 index 0000000000..bf44068c45 --- /dev/null +++ b/docs/syntax/card-group.md @@ -0,0 +1,40 @@ +# Card group + +A section heading + a card grid container. Wraps one or more [`{link-card}`](link-card.md) directives. + +## Basic + +```markdown +::::{card-group} +:title: Install and administer +:id: install + +:::{link-card} +title: Self-managed +link: /deploy/self-managed +description: Run on your own infrastructure. +links: + - { label: Docker, url: /deploy/docker } +::: + +:::{link-card} +title: Elastic Cloud Hosted +link: /deploy/cloud +description: Managed deployments on AWS, GCP, or Azure. +::: +:::: +``` + +The outer fence uses **four** colons (`::::`) so the inner three-colon `:::` fences for the cards aren't interpreted as a closing fence. Use as many extra colons on the outer fence as you need to nest cleanly. + +## Options + +| Option | Notes | +|---|---| +| `:title:` | H2 heading. Optional — without it, only the grid renders. | +| `:intro:` | Optional intro paragraph below the heading. | +| `:id:` | Section anchor. Picked up by [`{on-this-page}`](on-this-page.md). | + +## Layout + +The grid auto-fills 1, 2, or 3 columns based on viewport width using `grid-template-columns: repeat(auto-fill, minmax(310px, 1fr))`. Card heights match within a row. diff --git a/docs/syntax/hero.md b/docs/syntax/hero.md new file mode 100644 index 0000000000..4c245b4c66 --- /dev/null +++ b/docs/syntax/hero.md @@ -0,0 +1,60 @@ +# Hero + +A full-bleed hero band with a product icon, page title, description, fake search box, version dropdown, quick-action pills, and an optional release-status line. Designed for the [hub layout](hub-pages.md) but reusable on any page. + +All hero content is supplied via options. The directive body is not used. + +## Basic + +```markdown +:::{hero} +:icon: kibana +:title: Kibana +:description: The UI for the Elasticsearch platform. +:version: v9 / Serverless (current) +::: +``` + +The `:title:` option doubles as the page title (no body H1 needed). `:description:` supports inline markdown — links, bold, emphasis. + +## Options + +| Option | Type | Notes | +|---|---|---| +| `:title:` | string | **Required.** Page heading shown next to the icon. Also picked up as the document's page title. | +| `:description:` | inline markdown | One-line summary shown below the title. Supports bold, italics, and links. | +| `:icon:` | string | Product key. Resolves to an inline SVG via the product-icon lookup. Known keys: `elasticsearch`, `kibana`, `observability`, `security`. Unknown keys fall back to a single-letter chip. | +| `:version:` | string | Label inside the version chip (e.g. `v9 / Serverless (current)`). | +| `:versions:` | comma list | When set, the chip becomes a `
` dropdown listing other versions. Items use `Label` (greyed, "soon") or `Label=URL` (clickable). | +| `:quick-links:` | comma list, `Label=URL` | Pill bar below the version chip. | +| `:releases:` | inline markdown | Small status line under the pills, supports inline links and bold/italic. | +| `:search:` | bool, default `true` | Show the placeholder search box. | + +## Version dropdown + +```markdown +:::{hero} +:icon: kibana +:title: Kibana +:description: The UI for the Elasticsearch platform. +:version: v9 / Serverless (current) +:versions: v8=https://www.elastic.co/docs/products/kibana/8.x,v7 +::: +``` + +`v8=...` is a clickable link. `v7` (no `=URL`) renders greyed out with a "soon" badge. + +## Quick links and releases + +```markdown +:::{hero} +:icon: elasticsearch +:title: Elasticsearch +:description: The distributed search and analytics engine. +:version: v9 / Serverless (current) +:quick-links: Install=/install,API reference=/api,Release notes=/release-notes +:releases: Latest: [Stack 9.4.1](/rn) (Mar 28, 2026) · [Serverless deployed](/srn) Apr 1, 2026 +::: +``` + +The `:releases:` option is inline markdown — same syntax as a single line of body markdown. Use `:` for a literal colon when you don't want YAML to parse the value as a key/value pair. diff --git a/docs/syntax/hub-pages.md b/docs/syntax/hub-pages.md new file mode 100644 index 0000000000..71d3a9aaee --- /dev/null +++ b/docs/syntax/hub-pages.md @@ -0,0 +1,87 @@ +--- +products: + - id: elasticsearch + - id: kibana +--- + +# Hub pages + +Hub pages are product-scoped landing pages — a 360° overview of one product across versions, deployment types, and surfaces. They're enabled by setting `layout: hub` in the page's frontmatter and composed from a small set of dedicated directives. + +## Enable the layout + +```yaml +--- +layout: hub +--- +``` + +The `hub` layout drops the right-rail "On this page" TOC and the prev/next nav. The body owns the full width of the content column so directives can render full-bleed sections. + +## Directives + +| Directive | Purpose | +|-----------|---------| +| [`{hero}`](hero.md) | Full-bleed page hero with product icon, title, search, version dropdown, and quick-action pills. | +| [`{on-this-page}`](on-this-page.md) | Auto-generated inline TOC linking each section anchor on the page. | +| [`{intro}`](intro.md) | Small "getting started" callout panel with a teal accent bar. | +| [`{whats-new}`](whats-new.md) | "What's new" panel populated from `config/whats-new.yml`. | +| [`{card-group}`](card-group.md) | Section heading + card grid container. | +| [`{link-card}`](link-card.md) | Rich card with title, description, primary link list, and optional aside. | + +## Page skeleton + +```markdown +--- +layout: hub +--- + +:::{hero} +:icon: kibana +:title: Kibana +:description: The UI for the Elasticsearch platform. +:version: v9 / Serverless (current) +:versions: v8,v7 +:quick-links: Install=/install,Tutorial=/tutorial +:releases: Latest: [Stack 9.4.1](/rn) (Mar 28, 2026) +::: + +:::{on-this-page} +::: + +:::{intro} +**New to Kibana?** [Take the tutorial](/tutorial) — 30 minutes, hands-on. +::: + +:::{whats-new} +:product: kibana +::: + +::::{card-group} +:title: Install and administer +:id: install + +:::{link-card} +title: Self-managed +link: /deploy-manage/deploy/self-managed +description: Run Kibana on your own infrastructure. +links: + - { label: Docker, url: /deploy/docker } + - { label: Configure, url: /deploy/configure } +::: +:::: +``` + +## Product badges + +Every regular (non-hub) page that declares one or more `products:` in its frontmatter automatically gets a clickable badge above its H1, linking to that product's hub page. The badge → hub URL mapping is set per product in [`config/products.yml`](../configure/site/products.md): + +```yaml +products: + kibana: + display: 'Kibana' + versioning: 'stack' + hub: 'products/kibana/9.0' # path slug -- relative to site root +``` + +When `hub:` is null, no badge renders for that product. diff --git a/docs/syntax/intro.md b/docs/syntax/intro.md new file mode 100644 index 0000000000..9d4ca14cf3 --- /dev/null +++ b/docs/syntax/intro.md @@ -0,0 +1,19 @@ +# Intro callout + +A small white panel with a teal accent bar — used for "getting started" callouts directly under the hero. + +## Basic + +```markdown +:::{intro} +**New to Kibana?** [Take the tutorial](/tutorial) — a 30-minute, hands-on walk-through of Discover, dashboards, and ES|QL. +::: +``` + +Body is rendered as inline markdown. Use bold, links, and emphasis freely; keep it to one or two short paragraphs. + +## When to use + +`{intro}` is the standard "front door" panel between the [`{hero}`](hero.md) and the [`{on-this-page}`](on-this-page.md) chip. It's optional — skip it when the hero already says what readers need to know next. + +It is **not** an admonition. For caution / note / tip-style callouts inside body content, use the existing [admonitions](admonitions.md). diff --git a/docs/syntax/link-card.md b/docs/syntax/link-card.md new file mode 100644 index 0000000000..5b815522bf --- /dev/null +++ b/docs/syntax/link-card.md @@ -0,0 +1,74 @@ +# Link card + +A rich card with a title, description, primary link list, and an optional aside. Designed to live inside a [`{card-group}`](card-group.md), but renders standalone too. + +## Basic + +```markdown +:::{link-card} +title: Discover +link: /discover +description: Browse documents, filter, and query your indices in real time. +links: + - label: Get started with Discover + url: /discover/get-started + - label: Use ES|QL in Kibana + url: /esql +::: +``` + +The card body is **YAML, not markdown** — the directive expects a fixed schema and renders it. Authors don't write HTML, fences, or any directive options; they fill in fields. + +## Schema + +```yaml +title: Discover # required -- card heading +link: /discover/ # optional -- makes the title clickable +description: One-paragraph blurb. # optional +icon: elasticsearch # optional -- product-keyed inline SVG +variant: es # optional accent: es | obs | sec +links: # optional -- primary link list + - label: Get started + url: /discover/get-started + - label: Use ES|QL + url: /esql +aside: # optional bottom rail + label: Panel types + links: + - { label: Visualizations, url: /viz } + - { label: Maps, url: /maps } + - { label: Text, url: /text } +``` + +## Variants + +`variant: es | obs | sec` adds a left-border accent in the corresponding solution color (yellow / pink / teal). Use for solution cards. + +```markdown +:::{link-card} +title: Elasticsearch +link: /solutions/elasticsearch +icon: elasticsearch +variant: es +description: Build powerful search and RAG applications. +links: + - { label: Solution overview, url: /solutions/elasticsearch } + - { label: Get started, url: /solutions/elasticsearch/get-started } +aside: + label: Key features + links: + - { label: Vector search, url: /solutions/search/vector } + - { label: Semantic search, url: /solutions/search/semantic } +::: +``` + +## Aside + +The optional `aside` adds a separator line and a small label + dot-separated inline link list. Use it for supplementary references that don't fit the primary link list (e.g. "Panel types", "Chart types", "Key features"). + +## Errors + +The directive emits build errors when: + +- The body isn't valid YAML. +- `title` is missing or empty. diff --git a/docs/syntax/on-this-page.md b/docs/syntax/on-this-page.md new file mode 100644 index 0000000000..1674608297 --- /dev/null +++ b/docs/syntax/on-this-page.md @@ -0,0 +1,25 @@ +# On this page + +Inline TOC chip. Auto-collects every [`{card-group}`](card-group.md) and [`{whats-new}`](whats-new.md) on the same page that has both an `id` and a `title`, and renders them as middle-dot-separated anchor links inside a single rounded panel. + +## Basic + +```markdown +:::{on-this-page} +::: +``` + +That's the whole directive — no options, no body. Drop it once on a hub page (typically right after the [`{hero}`](hero.md)) and it stays in sync as you add or remove sections. + +## How items are collected + +For each section directive on the page, an item is added to the chip when: + +- The directive declares an `id` (becomes the anchor — `#install`). +- The directive declares a `title` (becomes the link label). + +`{card-group}`s with no `:title:` are skipped. `{whats-new}` panels populated from `config/whats-new.yml` get their `id` and `title` from that file; if the entry sets neither, the `{whats-new}` block is skipped. + +## Order + +Items appear in document order — top to bottom of the page. diff --git a/docs/syntax/whats-new.md b/docs/syntax/whats-new.md new file mode 100644 index 0000000000..e13a1c1b0b --- /dev/null +++ b/docs/syntax/whats-new.md @@ -0,0 +1,60 @@ +# What's new + +A panel with a "New" badge, section title, optional release-notes link list on the right, and rows of recent highlights. Each row has a title, description, and right-aligned meta pill (e.g. `9.4 preview`, `Mar 2026`). + +## Centralized lookup (recommended) + +Edit content in one place — [`config/whats-new.yml`](../configure/site/products.md) — and any page can render a product's panel with a one-line directive: + +```markdown +:::{whats-new} +:product: kibana +::: +``` + +`config/whats-new.yml` schema: + +```yaml +products: + kibana: + title: What's new in Kibana + id: whats-new # used as the section anchor + badge: New # optional, default 'New' + release-links: + - { label: '9.4', url: /release-notes/kibana } + - { label: Serverless, url: /release-notes/serverless } + items: + - title: Dashboards APIs + description: Programmatically create and manage dashboards + link: /api/dashboards + meta: 9.4 preview + - title: AI agent skills + description: Teach AI coding agents to work with the Elastic stack + link: /ai/agent-skills + meta: Mar 2026 +``` + +## Inline override + +If `:product:` is omitted, the directive expects the same schema as a YAML body. Useful when the page needs a one-off feed that doesn't belong in the central file: + +```markdown +:::{whats-new} +title: What's new in this release +items: + - title: Custom feature A + description: Details + link: /feature-a + meta: 9.4 +::: +``` + +## Options + +| Option | Notes | +|---|---| +| `:product:` | Product key from `config/whats-new.yml`. When set, the body is ignored. | + +## Anchor + +`{whats-new}` registers its `id` as a section anchor — `{on-this-page}` automatically picks it up alongside `{card-group}` sections. diff --git a/docs/testing/products/elasticsearch/v9.md b/docs/testing/products/elasticsearch/v9.md new file mode 100644 index 0000000000..8c0caa1aca --- /dev/null +++ b/docs/testing/products/elasticsearch/v9.md @@ -0,0 +1,610 @@ +--- +layout: hub +--- + +:::{hero} +:icon: elasticsearch +:title: Elasticsearch +:description: The distributed search and analytics engine. Store, search, and analyze data at any scale, with vector search, AI toolkits, and a high-performance retrieval engine. +:version: v9 / Serverless (current) +:versions: v8,v7 +:quick-links: Install=https://www.elastic.co/docs/deploy-manage/deploy/self-managed/installing-elasticsearch,Quick start=https://www.elastic.co/docs/reference/elasticsearch/rest-apis/api-examples,API reference=https://www.elastic.co/docs/api/doc/elasticsearch,Release notes=https://www.elastic.co/docs/release-notes/elasticsearch +:releases: Latest: [Stack 9.4.1](https://www.elastic.co/docs/release-notes/elasticsearch) (Mar 28, 2026) · [Serverless deployed](https://www.elastic.co/docs/release-notes/serverless) Apr 1, 2026 +::: + +:::{on-this-page} +::: + +:::{intro} +**New to Elasticsearch?** [Follow the quick start](https://www.elastic.co/docs/reference/elasticsearch/rest-apis/api-examples) to run your first queries, or explore [how to bring your data in](https://www.elastic.co/docs/manage-data/ingest) and start searching in minutes. +::: + +:::{whats-new} +:product: elasticsearch +::: + +::::{card-group} +:title: Install and deploy +:id: install + +:::{link-card} +title: Self-managed +link: https://www.elastic.co/docs/deploy-manage/deploy/self-managed/installing-elasticsearch +description: Install and run Elasticsearch on your own infrastructure. +links: + - label: Docker + url: https://www.elastic.co/docs/deploy-manage/deploy/self-managed/install-elasticsearch-with-docker + - label: Debian / Ubuntu + url: https://www.elastic.co/docs/deploy-manage/deploy/self-managed/install-elasticsearch-with-debian-package + - label: RPM + url: https://www.elastic.co/docs/deploy-manage/deploy/self-managed/install-elasticsearch-with-rpm + - label: Windows + url: https://www.elastic.co/docs/deploy-manage/deploy/self-managed/install-elasticsearch-with-zip-on-windows + - label: Linux / macOS archive + url: https://www.elastic.co/docs/deploy-manage/deploy/self-managed/install-elasticsearch-from-archive-on-linux-macos + - label: Configure Elasticsearch + url: https://www.elastic.co/docs/deploy-manage/deploy/self-managed/configure-elasticsearch + - label: Secure a self-managed cluster + url: https://www.elastic.co/docs/deploy-manage/deploy/self-managed/tutorial-self-managed-secure +::: + +:::{link-card} +title: Managed deployments +link: https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud +description: Run Elasticsearch on Elastic Cloud, Kubernetes, or ECE. +links: + - label: Elastic Cloud Hosted + url: https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud + - label: Elastic Cloud on Kubernetes (ECK) + url: https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s + - label: Elastic Cloud Enterprise (ECE) + url: https://www.elastic.co/docs/deploy-manage/deploy/cloud-enterprise +::: + +:::{link-card} +title: Maintain and monitor +link: https://www.elastic.co/docs/deploy-manage/maintenance/start-stop-services/start-stop-elasticsearch +description: Production guidance, upgrades, cluster health, and diagnostics. +links: + - label: Start and stop Elasticsearch + url: https://www.elastic.co/docs/deploy-manage/maintenance/start-stop-services/start-stop-elasticsearch + - label: Stack monitoring + url: https://www.elastic.co/docs/deploy-manage/monitor/stack-monitoring/elasticsearch-monitoring-self-managed + - label: Capture diagnostics + url: https://www.elastic.co/docs/troubleshoot/elasticsearch/diagnostic + - label: Troubleshoot + url: https://www.elastic.co/docs/troubleshoot/elasticsearch + - label: Snapshot and restore + url: https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore +::: +:::: + +::::{card-group} +:title: Index and ingest data +:id: ingest + +:::{link-card} +title: Ingest pipelines +link: https://www.elastic.co/docs/manage-data/ingest/transform-enrich/ingest-pipelines +description: Pre-process documents before indexing with processors that parse, transform, and enrich your data. +links: + - label: Overview and setup + url: https://www.elastic.co/docs/manage-data/ingest/transform-enrich/ingest-pipelines + - label: Simulate ingestion (API) + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-simulate-ingest + - label: All ingest methods + url: https://www.elastic.co/docs/manage-data/ingest +::: + +:::{link-card} +title: Data streams +link: https://www.elastic.co/docs/manage-data/data-store/data-streams +description: Manage time-series data with a single resource that spans multiple backing indices. +links: + - label: Data streams overview + url: https://www.elastic.co/docs/manage-data/data-store/data-streams + - label: Manage data streams + url: https://www.elastic.co/docs/manage-data/data-store/data-streams/manage-data-stream + - label: Logs data stream (logsdb) + url: https://www.elastic.co/docs/manage-data/data-store/data-streams/logs-data-stream +::: + +:::{link-card} +title: Document APIs +link: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-index +description: Index, update, delete, and retrieve documents with the core document APIs. +links: + - label: Index a document + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-index + - label: Bulk API + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk + - label: Reindex API + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-reindex + - label: Reindex examples + url: https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reindex-indices + - label: Migrate data between clusters + url: https://www.elastic.co/docs/manage-data/migrate/migrate-data-between-elasticsearch-clusters-with-minimal-downtime +::: +:::: + +::::{card-group} +:title: Search and query +:id: search + +:::{link-card} +title: Query DSL +link: https://www.elastic.co/docs/reference/query-languages/query-dsl/full-text-queries +description: Write expressive queries using the JSON-based Domain Specific Language — from simple term filters to compound boolean queries. +links: + - label: Full-text search + url: https://www.elastic.co/docs/solutions/search/full-text + - label: Full-text queries + url: https://www.elastic.co/docs/reference/query-languages/query-dsl/full-text-queries + - label: Search API + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-search + - label: Cross-cluster search + url: https://www.elastic.co/docs/explore-analyze/cross-cluster-search +aside: + label: Query types + links: + - label: Full-text + url: https://www.elastic.co/docs/reference/query-languages/query-dsl/full-text-queries + - label: Term-level + url: https://www.elastic.co/docs/reference/query-languages/query-dsl + - label: Geo + url: https://www.elastic.co/docs/reference/query-languages/query-dsl + - label: Shape + url: https://www.elastic.co/docs/reference/query-languages/query-dsl + - label: Joining + url: https://www.elastic.co/docs/reference/query-languages/query-dsl + - label: Span + url: https://www.elastic.co/docs/reference/query-languages/query-dsl +::: + +:::{link-card} +title: ES|QL +link: https://www.elastic.co/docs/reference/query-languages/esql/esql-getting-started +description: A pipe-based query language purpose-built for filtering, transforming, and analyzing data — usable from the API, Kibana Discover, or directly in search. +links: + - label: Get started with ES|QL + url: https://www.elastic.co/docs/reference/query-languages/esql/esql-getting-started + - label: Syntax reference + url: https://www.elastic.co/docs/reference/query-languages/esql/esql-syntax-reference + - label: Commands + url: https://www.elastic.co/docs/reference/query-languages/esql/esql-commands + - label: ES|QL for search + url: https://www.elastic.co/docs/solutions/search/esql-for-search + - label: Cross-cluster ES|QL + url: https://www.elastic.co/docs/reference/query-languages/esql/esql-cross-clusters +::: + +:::{link-card} +title: Other query languages +link: https://www.elastic.co/docs/reference/query-languages +description: Use EQL for event sequences, SQL for familiar syntax, or Painless for custom scoring and scripted queries. +links: + - label: EQL (Event Query Language) + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-eql-search + - label: SQL + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-sql-query + - label: Translate SQL to Query DSL + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-sql-translate + - label: Painless scripting + url: https://www.elastic.co/docs/explore-analyze/scripting/modules-scripting-painless +::: +:::: + +::::{card-group} +:title: Aggregations +:id: aggs + +:::{link-card} +title: Aggregations overview +link: https://www.elastic.co/docs/explore-analyze/query-filter/aggregations +description: Summarize and compute statistics over your data — counts, averages, histograms, date ranges, and more. +links: + - label: Introduction + url: https://www.elastic.co/docs/explore-analyze/query-filter/aggregations + - label: 'Tutorial: eCommerce data analysis' + url: https://www.elastic.co/docs/explore-analyze/query-filter/aggregations/tutorial-analyze-ecommerce-data-with-aggregations-using-query-dsl +aside: + label: Aggregation families + links: + - label: Metric + url: https://www.elastic.co/docs/reference/aggregations + - label: Bucket + url: https://www.elastic.co/docs/reference/aggregations + - label: Pipeline + url: https://www.elastic.co/docs/reference/aggregations + - label: Matrix + url: https://www.elastic.co/docs/reference/aggregations +::: + +:::{link-card} +title: Metric aggregations +link: https://www.elastic.co/docs/reference/aggregations +description: Compute single numeric values from field data — min, max, avg, sum, percentiles, stats, and more. +links: + - label: Top hits + url: https://www.elastic.co/docs/reference/aggregations/search-aggregations-metrics-top-hits-aggregation + - label: Top metrics + url: https://www.elastic.co/docs/reference/aggregations/search-aggregations-metrics-top-metrics + - label: Cardinality, percentiles, stats + url: https://www.elastic.co/docs/reference/aggregations +::: + +:::{link-card} +title: Bucket and pipeline aggregations +link: https://www.elastic.co/docs/reference/aggregations +description: Group documents into buckets, then chain pipeline aggregations to compute on those buckets. +links: + - label: Filters aggregation + url: https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-filters-aggregation + - label: Multi-terms aggregation + url: https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-multi-terms-aggregation + - label: Avg bucket (pipeline) + url: https://www.elastic.co/docs/reference/aggregations/search-aggregations-pipeline-avg-bucket-aggregation + - label: Bucket script (pipeline) + url: https://www.elastic.co/docs/reference/aggregations/search-aggregations-pipeline-bucket-script-aggregation +::: +:::: + +::::{card-group} +:title: Data modeling +:id: modeling + +:::{link-card} +title: Mapping +link: https://www.elastic.co/docs/reference/elasticsearch/mapping-reference +description: Define how documents and their fields are stored, indexed, and analyzed. +links: + - label: Field data types + url: https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/field-data-types + - label: Dynamic field mapping + url: https://www.elastic.co/docs/manage-data/data-store/mapping/dynamic-field-mapping + - label: Runtime fields + url: https://www.elastic.co/docs/manage-data/data-store/mapping/define-runtime-fields-in-search-request + - label: Update mappings (examples) + url: https://www.elastic.co/docs/manage-data/data-store/mapping/update-mappings-examples + - label: Put mapping API + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-put-mapping +aside: + label: Common field types + links: + - label: keyword + url: https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/field-data-types + - label: text + url: https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/field-data-types + - label: date + url: https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/field-data-types + - label: dense_vector + url: https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/field-data-types + - label: semantic_text + url: https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/field-data-types + - label: geo_point + url: https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/field-data-types +::: + +:::{link-card} +title: Text analysis +link: https://www.elastic.co/docs/manage-data/data-store/text-analysis/configure-text-analysis +description: Control how text is tokenized and indexed to optimize relevance and search recall. +links: + - label: Configure text analysis + url: https://www.elastic.co/docs/manage-data/data-store/text-analysis/configure-text-analysis + - label: Create a custom analyzer + url: https://www.elastic.co/docs/manage-data/data-store/text-analysis/create-custom-analyzer + - label: Specify an analyzer + url: https://www.elastic.co/docs/manage-data/data-store/text-analysis/specify-an-analyzer + - label: Analyze API + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-analyze + - label: Analyzers, tokenizers, and filters reference + url: https://www.elastic.co/docs/reference/text-analysis +::: + +:::{link-card} +title: Configuration reference +link: https://www.elastic.co/docs/reference/elasticsearch/configuration-reference +description: All Elasticsearch node settings — network, thread pools, index defaults, security, and more. +links: + - label: Node settings + url: https://www.elastic.co/docs/reference/elasticsearch/configuration-reference/node-settings + - label: Security settings + url: https://www.elastic.co/docs/reference/elasticsearch/configuration-reference/security-settings + - label: All settings reference + url: https://www.elastic.co/docs/reference/elasticsearch/configuration-reference +::: +:::: + +::::{card-group} +:title: Manage data +:id: manage + +:::{link-card} +title: Index lifecycle management (ILM) +link: https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management +description: Automate index policies to move data through hot, warm, cold, and frozen tiers — and eventually delete it. +links: + - label: Create an ILM policy + url: https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management/configure-lifecycle-policy + - label: Tutorials + url: https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management/ilm-tutorials + - label: Check ILM status + url: https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management/policy-view-status + - label: Start and stop ILM + url: https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management/start-stop-index-lifecycle-management + - label: Explain lifecycle (API) + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-ilm-explain-lifecycle +::: + +:::{link-card} +title: Data stream lifecycle +link: https://www.elastic.co/docs/manage-data/lifecycle/data-stream +description: Set built-in retention and rollover directly on data streams, without a separate ILM policy. +links: + - label: Data stream lifecycle overview + url: https://www.elastic.co/docs/manage-data/lifecycle/data-stream + - label: Update an existing data stream + url: https://www.elastic.co/docs/manage-data/lifecycle/data-stream/tutorial-update-existing-data-stream + - label: Manage data tiers + url: https://www.elastic.co/docs/manage-data/lifecycle/data-tiers/manage-data-tiers-self-managed-eck +::: + +:::{link-card} +title: Snapshot and restore +link: https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore +description: Back up indices and data streams to a remote repository and restore them when needed. +links: + - label: Overview + url: https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore + - label: Restore a snapshot + url: https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/restore-snapshot + - label: Create a snapshot repository (API) + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-snapshot-create-repository + - label: Create a snapshot (API) + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-snapshot-create + - label: Troubleshoot snapshots + url: https://www.elastic.co/docs/troubleshoot/elasticsearch/snapshot-and-restore +::: +:::: + +::::{card-group} +:title: AI and vector search +:id: ai + +:::{link-card} +title: Semantic search +link: https://www.elastic.co/docs/solutions/search/semantic-search/semantic-search-semantic-text +description: Search by meaning rather than exact keywords using dense or sparse vector embeddings. +links: + - label: Semantic search with semantic_text + url: https://www.elastic.co/docs/solutions/search/semantic-search/semantic-search-semantic-text + - label: Semantic search with ELSER + url: https://www.elastic.co/docs/solutions/search/semantic-search/semantic-search-elser-ingest-pipelines + - label: Get started with semantic search + url: https://www.elastic.co/docs/solutions/search/get-started/semantic-search + - label: Dense vs. sparse ingest pipelines + url: https://www.elastic.co/docs/solutions/search/vector/dense-versus-sparse-ingest-pipelines +::: + +:::{link-card} +title: Vector search (kNN) +link: https://www.elastic.co/docs/solutions/search/vector/knn +description: Find the nearest neighbors to a query vector for similarity-based retrieval and hybrid search. +links: + - label: kNN search + url: https://www.elastic.co/docs/solutions/search/vector/knn + - label: Sparse vector search + url: https://www.elastic.co/docs/solutions/search/vector/sparse-vector + - label: Bring your own vectors + url: https://www.elastic.co/docs/solutions/search/vector/bring-own-vectors + - label: kNN retriever + url: https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/knn-retriever + - label: kNN in ES|QL + url: https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/dense-vector-functions/knn +::: + +:::{link-card} +title: Inference API +link: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put +description: Connect Elasticsearch to external AI model providers for embeddings, reranking, and completions. +links: + - label: Create an inference endpoint + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put + - label: ELSER inference endpoint + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-elser +aside: + label: Supported providers + links: + - label: OpenAI + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put + - label: Cohere + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put + - label: Anthropic + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put + - label: Azure + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put + - label: Google + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put + - label: Hugging Face + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put + - label: Mistral + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put +::: +:::: + +::::{card-group} +:title: Security +:id: security + +:::{link-card} +title: Encryption and TLS +link: https://www.elastic.co/docs/deploy-manage/security/secure-cluster-communications +description: Encrypt communications between nodes and between clients and the cluster. +links: + - label: Set up basic security (TLS) + url: https://www.elastic.co/docs/deploy-manage/security/set-up-basic-security + - label: Secure cluster communications + url: https://www.elastic.co/docs/deploy-manage/security/secure-cluster-communications + - label: Secure HTTP clients + url: https://www.elastic.co/docs/deploy-manage/security/httprest-clients-security + - label: Security settings reference + url: https://www.elastic.co/docs/reference/elasticsearch/configuration-reference/security-settings +::: + +:::{link-card} +title: Authentication +link: https://www.elastic.co/docs/deploy-manage/users-roles/cluster-or-deployment-auth/ldap +description: Verify user identity with native realms, LDAP, Active Directory, SAML, OIDC, PKI, and more. +links: + - label: LDAP authentication + url: https://www.elastic.co/docs/deploy-manage/users-roles/cluster-or-deployment-auth/ldap + - label: SAML authentication (API) + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-security-saml-authenticate + - label: All authentication methods + url: https://www.elastic.co/docs/deploy-manage/users-roles/cluster-or-deployment-auth +::: + +:::{link-card} +title: Authorization and RBAC +link: https://www.elastic.co/docs/deploy-manage/users-roles +description: Control what users and applications can access using roles, privileges, and field- and document-level security. +links: + - label: Users and roles overview + url: https://www.elastic.co/docs/deploy-manage/users-roles + - label: Create or update a role (API) + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-security-put-role + - label: Create or update a user (API) + url: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-security-put-user +::: +:::: + +::::{card-group} +:title: Clients and integrations +:id: clients + +:::{link-card} +title: Language clients +link: https://www.elastic.co/docs/reference/elasticsearch/clients +description: Official Elasticsearch clients for your preferred programming language. +links: + - label: Python + url: https://www.elastic.co/docs/reference/elasticsearch/clients/python + - label: Java + url: https://www.elastic.co/docs/reference/elasticsearch/clients/java/getting-started + - label: Go + url: https://www.elastic.co/docs/reference/elasticsearch/clients/go/using-the-api/dense-vectors + - label: Rust + url: https://www.elastic.co/docs/reference/elasticsearch/clients/rust + - label: PHP + url: https://www.elastic.co/docs/reference/elasticsearch/clients/php + - label: Ruby + url: https://www.elastic.co/docs/reference/elasticsearch/clients/ruby/api + - label: Community clients + url: https://www.elastic.co/docs/reference/elasticsearch/clients/community +::: + +:::{link-card} +title: Python DSL and helpers +link: https://www.elastic.co/docs/reference/elasticsearch/clients/python/elasticsearch-dsl +description: High-level Python libraries that simplify building queries, mappings, and bulk operations. +links: + - label: Elasticsearch DSL (Python) + url: https://www.elastic.co/docs/reference/elasticsearch/clients/python/elasticsearch-dsl + - label: Java bulk indexing + url: https://www.elastic.co/docs/reference/elasticsearch/clients/java/usage/indexing-bulk + - label: Troubleshoot clients + url: https://www.elastic.co/docs/troubleshoot/elasticsearch/clients +::: + +:::{link-card} +title: Ingest tools +link: https://www.elastic.co/docs/manage-data/ingest +description: Get data into Elasticsearch using Fleet, Logstash, Beats, or search connectors. +links: + - label: Fleet and Elastic Agent + url: https://www.elastic.co/docs/reference/fleet + - label: Content connectors + url: https://www.elastic.co/docs/reference/search-connectors + - label: All ingest options + url: https://www.elastic.co/docs/manage-data/ingest +::: +:::: + +::::{card-group} +:title: Reference +:id: ref + +:::{link-card} +title: REST APIs +link: https://www.elastic.co/docs/reference/elasticsearch/rest-apis +description: Conventions, common options, compatibility guarantees, and the full interactive API specification. +links: + - label: API conventions + url: https://www.elastic.co/docs/reference/elasticsearch/rest-apis/api-conventions + - label: Common options + url: https://www.elastic.co/docs/reference/elasticsearch/rest-apis/common-options + - label: API compatibility + url: https://www.elastic.co/docs/reference/elasticsearch/rest-apis/compatibility + - label: Examples and quick start + url: https://www.elastic.co/docs/reference/elasticsearch/rest-apis/api-examples + - label: Full API reference + url: https://www.elastic.co/docs/api/doc/elasticsearch +::: + +:::{link-card} +title: Painless scripting +link: https://www.elastic.co/docs/explore-analyze/scripting/modules-scripting-painless +description: Write custom scripts in Painless for queries, aggregations, ingest processors, and runtime fields. +links: + - label: Introduction to Painless + url: https://www.elastic.co/docs/explore-analyze/scripting/modules-scripting-painless + - label: Language specification + url: https://www.elastic.co/docs/reference/scripting-languages/painless/painless-language-specification + - label: Use Painless in runtime fields + url: https://www.elastic.co/docs/reference/scripting-languages/painless/use-painless-scripts-in-runtime-fields + - label: Scripts reference + url: https://www.elastic.co/docs/reference/scripting-languages/painless/painless-scripts +::: + +:::{link-card} +title: Release notes +link: https://www.elastic.co/docs/release-notes/elasticsearch +description: What's new, deprecated, and fixed in each Elasticsearch release. +links: + - label: Elasticsearch + url: https://www.elastic.co/docs/release-notes/elasticsearch + - label: Breaking changes + url: https://www.elastic.co/docs/release-notes/elasticsearch/breaking-changes + - label: Deprecations + url: https://www.elastic.co/docs/release-notes/elasticsearch/deprecations +aside: + label: Client release notes + links: + - label: Python + url: https://www.elastic.co/docs/release-notes/elasticsearch/clients/python + - label: Java + url: https://www.elastic.co/docs/release-notes/elasticsearch/clients/java + - label: .NET + url: https://www.elastic.co/docs/release-notes/elasticsearch/clients/dotnet + - label: PHP + url: https://www.elastic.co/docs/release-notes/elasticsearch/clients/php + - label: Ruby + url: https://www.elastic.co/docs/release-notes/elasticsearch/clients/ruby +::: + +:::{link-card} +title: Troubleshooting +link: https://www.elastic.co/docs/troubleshoot/elasticsearch +description: Diagnose and fix common Elasticsearch cluster, performance, and operational issues. +links: + - label: Cluster issues + url: https://www.elastic.co/docs/troubleshoot/elasticsearch/clusters + - label: Snapshot and restore + url: https://www.elastic.co/docs/troubleshoot/elasticsearch/snapshot-and-restore + - label: Clients + url: https://www.elastic.co/docs/troubleshoot/elasticsearch/clients + - label: Capture diagnostics + url: https://www.elastic.co/docs/troubleshoot/elasticsearch/diagnostic +::: +:::: diff --git a/docs/testing/products/kibana/v9.md b/docs/testing/products/kibana/v9.md new file mode 100644 index 0000000000..360edb2f7e --- /dev/null +++ b/docs/testing/products/kibana/v9.md @@ -0,0 +1,473 @@ +--- +layout: hub +--- + +:::{hero} +:icon: kibana +:title: Kibana +:description: The UI for the Elasticsearch platform. Explore and visualize your data, build dashboards, set up alerts, automate tasks with AI, and use purpose-built solutions for Search, Observability, and Security. +:version: v9 / Serverless (current) +:versions: v8,v7 +:quick-links: Install=/install,Tutorial=/tutorial,API reference=/api,Release notes=/release-notes +:releases: Latest: [Stack 9.4.1](/release-notes/kibana) (Mar 28, 2026) · [Serverless deployed](/release-notes/serverless) Apr 1, 2026 +::: + +:::{on-this-page} +::: + +:::{intro} +**New to Kibana?** [Learn data exploration and visualization](https://www.elastic.co/docs/explore-analyze/kibana-data-exploration-learning-tutorial) — a hands-on walk-through of Discover, ES|QL, visualizations, and dashboards. +::: + +:::{whats-new} +:product: kibana +::: + +::::{card-group} +:title: Install and administer +:id: install + +:::{link-card} +title: Self-managed +link: /deploy-manage/deploy/self-managed/install-kibana +description: Install and run Kibana on your own infrastructure. +links: + - label: Docker + url: /deploy-manage/deploy/self-managed/install-kibana-with-docker + - label: Debian / Ubuntu + url: /deploy-manage/deploy/self-managed/install-kibana-with-debian-package + - label: RPM + url: /deploy-manage/deploy/self-managed/install-kibana-with-rpm + - label: Windows + url: /deploy-manage/deploy/self-managed/install-kibana-on-windows + - label: Configure (kibana.yml) + url: /deploy-manage/deploy/self-managed/configure-kibana +::: + +:::{link-card} +title: Managed deployments +link: /deploy-manage/deploy/elastic-cloud/access-kibana +description: Run Kibana on Elastic Cloud, Kubernetes, or ECE. +links: + - label: Elastic Cloud Hosted + url: /deploy-manage/deploy/elastic-cloud/access-kibana + - label: Elastic Cloud on Kubernetes (ECK) + url: /deploy-manage/deploy/cloud-on-k8s/kibana-configuration + - label: Elastic Cloud Enterprise + url: /deploy-manage/deploy/cloud-enterprise/access-kibana +::: + +:::{link-card} +title: Configure and secure +link: /deploy-manage/users-roles/cluster-or-deployment-auth/kibana-authentication +description: Authentication, roles, spaces, and encryption. +links: + - label: Authentication (SSO, SAML, OIDC...) + url: /deploy-manage/users-roles/cluster-or-deployment-auth/kibana-authentication + - label: Kibana privileges + url: /deploy-manage/users-roles/cluster-or-deployment-auth/kibana-privileges + - label: Spaces + url: /deploy-manage/manage-spaces + - label: Secure saved objects + url: /deploy-manage/security/secure-saved-objects +::: + +:::{link-card} +title: Maintain and monitor +link: /deploy-manage/production-guidance/kibana-in-production-environments +description: Production guidance, upgrades, reporting, monitoring, and logging. +links: + - label: Run in production + url: /deploy-manage/production-guidance/kibana-in-production-environments + - label: Upgrade Kibana + url: /deploy-manage/upgrade/deployment-or-cluster/kibana + - label: Monitoring + url: /deploy-manage/monitor/stack-monitoring/kibana-monitoring-data + - label: Logging + url: /deploy-manage/monitor/logging-configuration/kibana-logging +::: +:::: + +::::{card-group} +:title: Explore, visualize, and analyze +:id: explore + +:::{link-card} +title: Discover +link: /explore-analyze/discover/discover-get-started +description: Browse documents, filter, and query your indices in real time. +links: + - label: Get started with Discover + url: /explore-analyze/discover/discover-get-started + - label: Use ES|QL in Kibana + url: /explore-analyze/query-filter/languages/esql-kibana +::: + +:::{link-card} +title: Dashboards +link: /explore-analyze/dashboards +description: Build interactive dashboards that combine visualizations, controls, and context. +links: + - label: Create a dashboard + url: /explore-analyze/dashboards/create-dashboard + - label: Build and customize + url: /explore-analyze/dashboards/building + - label: Organize panels and sections + url: /explore-analyze/dashboards/arrange-panels + - label: Share and export + url: /explore-analyze/dashboards/sharing +aside: + label: Panel types + links: + - label: Visualizations + url: /explore-analyze/visualize/lens + - label: Maps + url: /explore-analyze/visualize/maps + - label: Text + url: /explore-analyze/visualize/text-panels + - label: Images + url: /explore-analyze/visualize/image-panels + - label: Links + url: /explore-analyze/visualize/link-panels + - label: Alerts + url: /explore-analyze/visualize/alert-panels +::: + +:::{link-card} +title: Build visualizations +link: /explore-analyze/visualize/lens +description: Create charts and visual panels using the drag-and-drop editor, query mode (ES|QL), Vega, or Maps. +links: + - label: Visualization editor (drag-and-drop) + url: /explore-analyze/visualize/lens + - label: Query mode (ES|QL) + url: /explore-analyze/visualize/esorql + - label: Vega + url: /explore-analyze/visualize/custom-visualizations-with-vega + - label: Maps + url: /explore-analyze/visualize/maps +aside: + label: Chart types + links: + - label: Area + url: /explore-analyze/visualize/charts/area-charts + - label: Line + url: /explore-analyze/visualize/charts/line-charts + - label: Pie + url: /explore-analyze/visualize/charts/pie-charts + - label: Table + url: /explore-analyze/visualize/charts/tables + - label: Metric + url: /explore-analyze/visualize/charts/metric-charts + - label: Heat map + url: /explore-analyze/visualize/charts/heat-map-charts + - label: Treemap + url: /explore-analyze/visualize/charts/treemap-charts + - label: Mosaic + url: /explore-analyze/visualize/charts/mosaic-charts +::: +:::: + +::::{card-group} +:title: Track and respond +:id: track + +:::{link-card} +title: Alerting and rules +link: https://www.elastic.co/docs/explore-analyze/alerting/alerts/alerting-getting-started +description: Detect important changes and notify people or trigger actions. +links: + - label: Getting started + url: https://www.elastic.co/docs/explore-analyze/alerting/alerts/alerting-getting-started + - label: Create and manage rules + url: https://www.elastic.co/docs/explore-analyze/alerting/alerts/create-manage-rules + - label: Performance and scaling + url: https://www.elastic.co/docs/deploy-manage/production-guidance/kibana-alerting-production-considerations +::: + +:::{link-card} +title: Connectors +link: https://www.elastic.co/docs/reference/kibana/connectors-kibana +description: Integrate Kibana with external services for AI, notifications, and case management. +links: + - label: GenAI (OpenAI, Bedrock, Gemini...) + url: https://www.elastic.co/docs/reference/kibana/connectors-kibana/gen-ai-connectors + - label: Alerting & cases (Jira, ServiceNow, PagerDuty...) + url: https://www.elastic.co/docs/reference/kibana/connectors-kibana/alerting-cases-connectors + - label: Elastic Stack + url: https://www.elastic.co/docs/reference/kibana/connectors-kibana + - label: Data & context sources + url: https://www.elastic.co/docs/reference/kibana/connectors-kibana +::: + +:::{link-card} +title: Reporting and sharing +link: https://www.elastic.co/docs/deploy-manage/kibana-reporting-configuration +description: Generate PDF, PNG, and CSV exports. Share dashboards with links or embeds. +links: + - label: Configure reporting + url: https://www.elastic.co/docs/deploy-manage/kibana-reporting-configuration + - label: Share dashboards + url: https://www.elastic.co/docs/explore-analyze/dashboards/sharing + - label: Saved objects + url: https://www.elastic.co/docs/explore-analyze/find-and-organize/saved-objects +::: +:::: + +::::{card-group} +:title: AI and automation +:id: ai + +:::{link-card} +title: Agent Builder +link: https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder +description: Build custom AI agents that reason over your Elasticsearch data using LLMs and tools. +links: + - label: Overview + url: https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder + - label: Agents + url: https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/agent-builder-agents + - label: Custom tools + url: https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/tools/custom-tools + - label: Programmatic access (MCP, A2A, API) + url: https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/programmatic-access + - label: 'Tutorial: build a custom agent' + url: https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/agent-builder-api-tutorial +::: + +:::{link-card} +title: Workflows +link: https://www.elastic.co/docs/explore-analyze/workflows +description: Automate tasks with sequences that chain Kibana actions, HTTP calls, and AI agents. +links: + - label: Get started + url: https://www.elastic.co/docs/explore-analyze/workflows/get-started + - label: Kibana action steps + url: https://www.elastic.co/docs/explore-analyze/workflows/steps/kibana + - label: Call agents from workflows + url: https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/agents-and-workflows +::: + +:::{link-card} +title: Machine learning +link: https://www.elastic.co/docs/explore-analyze/machine-learning/machine-learning-in-kibana +description: Detect anomalies, forecast trends, and run data frame analytics. +links: + - label: Machine learning in Kibana + url: https://www.elastic.co/docs/explore-analyze/machine-learning/machine-learning-in-kibana + - label: Anomaly detection job wizards + url: https://www.elastic.co/docs/reference/machine-learning/supplied-anomaly-detection-configurations +::: +:::: + +::::{card-group} +:title: Solutions +:id: solutions + +:::{link-card} +title: Elasticsearch +link: /solutions/elasticsearch-solution-project +icon: elasticsearch +variant: es +description: Build powerful search and RAG applications using vector database, AI toolkit, and advanced retrieval capabilities. +links: + - label: Solution overview + url: /solutions/elasticsearch-solution-project + - label: Get started + url: /solutions/elasticsearch-solution-project/get-started + - label: Agent Builder + url: /explore-analyze/ai-features/elastic-agent-builder +aside: + label: Key features + links: + - label: Playground + url: /explore-analyze/query-filter/tools/playground + - label: Vector search + url: /solutions/search/vector + - label: Content connectors + url: /reference/search-connectors + - label: Semantic search + url: /solutions/search/semantic-search/semantic-search-elser-ingest-pipelines +::: + +:::{link-card} +title: Observability +link: /solutions/observability +icon: observability +variant: obs +description: Resolve problems with open, flexible, and unified observability powered by advanced machine learning and analytics. +links: + - label: Solution overview + url: /solutions/observability + - label: Get started + url: /solutions/observability/get-started + - label: Agent Builder + url: /solutions/observability/ai/agent-builder-observability +aside: + label: Key features + links: + - label: APM + url: /solutions/observability/apm + - label: Logs + url: /solutions/observability/logs + - label: Infrastructure + url: /solutions/observability/infra-and-hosts + - label: Synthetics + url: /solutions/observability/synthetics/get-started + - label: SLOs + url: /solutions/observability/incident-management/service-level-objectives-slos +::: + +:::{link-card} +title: Security +link: /solutions/security +icon: security +variant: sec +description: Detect, investigate, and respond to threats with AI-driven security analytics to protect your organization. +links: + - label: Solution overview + url: /solutions/security + - label: Get started + url: /solutions/security/get-started + - label: Agent Builder + url: /solutions/security/ai/agent-builder/agent-builder +aside: + label: Key features + links: + - label: SIEM + url: /solutions/security/get-started/get-started-detect-with-siem + - label: Detection rules + url: /solutions/security/detect-and-alert/manage-detection-rules + - label: Elastic Defend + url: /solutions/security/configure-elastic-defend + - label: Cloud security + url: /solutions/security/cloud + - label: Cases + url: /solutions/security/investigate/security-cases +::: +:::: + +::::{card-group} +:title: Management and developer tools +:id: manage + +:::{link-card} +title: Data and indices +description: Manage Elasticsearch indices, data streams, ingest pipelines, and transforms from Kibana. +links: + - label: Index management + url: https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management/index-management-in-kibana + - label: Data streams + url: https://www.elastic.co/docs/manage-data/data-store/data-streams/manage-data-stream + - label: Transforms + url: https://www.elastic.co/docs/explore-analyze/transforms/transform-setup + - label: Task management + url: https://www.elastic.co/docs/deploy-manage/distributed-architecture/kibana-tasks-management +::: + +:::{link-card} +title: Integrations and Fleet +description: Deploy and manage Elastic Agents, integrations, and Osquery. +links: + - label: Fleet + url: https://www.elastic.co/docs/reference/fleet + - label: Manage agents + url: https://www.elastic.co/docs/reference/fleet/manage-elastic-agents-in-fleet + - label: Agent policies + url: https://www.elastic.co/docs/reference/fleet/agent-policy +::: + +:::{link-card} +title: Developer tools +description: Run API requests, profile queries, test grok patterns, and debug Painless scripts. +links: + - label: Console + url: https://www.elastic.co/docs/reference/cloud/cloud-hosted/ec-api-console + - label: Search Profiler + url: https://www.elastic.co/docs/explore-analyze/query-filter/tools/search-profiler + - label: Grok Debugger + url: https://www.elastic.co/docs/explore-analyze/query-filter/tools/grok-debugger +::: +:::: + +::::{card-group} +:title: Reference +:id: ref + +:::{link-card} +title: Configuration and settings +link: https://www.elastic.co/docs/reference/kibana/configuration-reference +description: All kibana.yml settings and UI-configurable advanced settings. +links: + - label: General + url: https://www.elastic.co/docs/reference/kibana/configuration-reference/general-settings + - label: Alerting + url: https://www.elastic.co/docs/reference/kibana/configuration-reference/alerting-settings + - label: Security + url: https://www.elastic.co/docs/reference/kibana/configuration-reference/security-settings + - label: Reporting + url: https://www.elastic.co/docs/reference/kibana/configuration-reference/reporting-settings + - label: Monitoring + url: https://www.elastic.co/docs/reference/kibana/configuration-reference/monitoring-settings + - label: Advanced settings (UI) + url: https://www.elastic.co/docs/reference/kibana/advanced-settings +::: + +:::{link-card} +title: APIs and plugins +link: https://www.elastic.co/docs/api/doc/kibana +description: REST API, plugins, and CLI commands. +links: + - label: Kibana REST API + url: https://www.elastic.co/docs/api/doc/kibana + - label: Kibana serverless API + url: https://www.elastic.co/docs/api/doc/serverless-kibana + - label: Plugins + url: https://www.elastic.co/docs/reference/kibana/kibana-plugins + - label: kibana-setup command + url: https://www.elastic.co/docs/reference/kibana/commands/kibana-setup +::: + +:::{link-card} +title: Release notes +link: https://www.elastic.co/docs/release-notes/kibana +description: What's new, deprecated, and fixed in each Kibana release. +links: + - label: Kibana + url: https://www.elastic.co/docs/release-notes/kibana + - label: Known issues + url: https://www.elastic.co/docs/release-notes/kibana/known-issues + - label: Breaking changes + url: https://www.elastic.co/docs/release-notes/kibana/breaking-changes + - label: Deprecations + url: https://www.elastic.co/docs/release-notes/kibana/deprecations +aside: + label: Related release notes + links: + - label: Elasticsearch + url: https://www.elastic.co/docs/release-notes/elasticsearch + - label: Security + url: https://www.elastic.co/docs/release-notes/security + - label: Observability + url: https://www.elastic.co/docs/release-notes/observability + - label: Serverless + url: https://www.elastic.co/docs/release-notes/serverless +::: + +:::{link-card} +title: Troubleshooting +link: https://www.elastic.co/docs/troubleshoot/kibana +description: Diagnose and fix common Kibana issues. +links: + - label: General + url: https://www.elastic.co/docs/troubleshoot/kibana + - label: Alerts + url: https://www.elastic.co/docs/troubleshoot/kibana/alerts + - label: Maps + url: https://www.elastic.co/docs/troubleshoot/kibana/maps + - label: Reporting + url: https://www.elastic.co/docs/troubleshoot/kibana/reporting + - label: Capture diagnostics + url: https://www.elastic.co/docs/troubleshoot/kibana/capturing-diagnostics +::: +:::: diff --git a/nav-v2-status.md b/nav-v2-status.md new file mode 100644 index 0000000000..6f13560244 --- /dev/null +++ b/nav-v2-status.md @@ -0,0 +1,122 @@ +# nav-v2 prototype — status & plan + +Branch: `nav-v2` +PR: https://github.com/elastic/docs-builder/pull/2927 (draft) +Last updated: 2026-03-24 + +--- + +## Goal + +Prototype a new information architecture sidebar without touching production nav. +Gated behind the `nav-v2` feature flag (auto-enabled in `dev` and `preview` environments). + +The V2 nav: +- Shows label sections (non-clickable headings) instead of bare TOC roots +- Renders the **full tree on every page** (not per-section like V1) +- Has accordion collapse (open one top-level section → others collapse) +- Auto-expands to the current page on direct URL load +- Disabled placeholder links styled with `cursor-not-allowed` +- Content built at **the same URL paths as V1** — only sidebar layout changes + +--- + +## Current state ✓ + +Build passes. Preview at https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/2927/ + +### New nav item types + +| Type | YAML key | C# type | Renders as | +|------|----------|---------|------------| +| Section heading | `label:` | `LabelNavigationNode` | Non-clickable uppercase header | +| Placeholder leaf | `title:` only | `PlaceholderNavigationLeaf` | Greyed link → generated stub page | +| Placeholder folder | `group:` (no `page:`) | `PlaceholderNavigationNode` | Greyed folder header → generated stub page | +| Page-folder | `group:` + `page:` | `PageFolderNavigationNode` | Clickable folder header with chevron | +| Cross-link leaf | `page:` | `PageCrossLinkLeaf` | Normal clickable link | +| TOC section | `toc:` | existing `IRootNavigationItem` | Full TOC tree (as V1) | + +### URL scheme + +V2 cannot control output file paths — `GlobalNavigationPathProvider` reads `NavigationTocMappings` from `navigation.yml` before `SiteNavigationV2` is constructed. So V2 reuses V1 URLs. Documented in PR description. + +Production migration path: wire `NavigationTocMappings` from the V2 file, with optional `path:` overrides on `toc:` entries to pin stable V1 URLs and avoid redirects. + +--- + +## Files changed + +### New files + +| File | Purpose | +|------|---------| +| `config/navigation-v2.yml` | Full IA structure — 6 top-level labels, nested labels, real `toc:`/`page:` wiring | +| `src/.../Toc/NavigationV2File.cs` | YAML model + `NavV2FileYamlConverter` | +| `src/.../V2/LabelNavigationNode.cs` | Non-clickable section heading | +| `src/.../V2/PlaceholderNavigationLeaf.cs` | Disabled placeholder link | +| `src/.../V2/PlaceholderNavigationNode.cs` | Disabled placeholder folder | +| `src/.../V2/PageCrossLinkLeaf.cs` | Real cross-link leaf (from `page:`) | +| `src/.../V2/PageFolderNavigationNode.cs` | Clickable folder with real URL (from `group:` + `page:`) | +| `src/.../V2/SiteNavigationV2.cs` | Extends `SiteNavigation`; builds V2 label tree | +| `src/.../Navigation/_TocTreeNavV2.cshtml` | V2 Razor partial | +| `src/.../Assets/pages-nav-v2.ts` | Accordion + current-page marking + auto-expand on load | + +### Modified files + +| File | Change | +|------|--------| +| `config/assembler.yml` | `NAV_V2: true` under `dev:` and `preview:` | +| `src/.../FeatureFlags.cs` | `NavV2Enabled` property | +| `src/.../ConfigurationFileProvider.cs` | `NavV2Deserializer`; `NavigationV2File` property | +| `src/.../NavigationViewModel.cs` | `IsNavV2` bool | +| `src/.../Navigation/_TocTree.cshtml` | Branches on `IsNavV2` | +| `src/.../AssemblerBuildService.cs` | Loads nav-v2 file; instantiates `SiteNavigationV2` | +| `src/.../GlobalNavigationHtmlWriter.cs` | Full V2 nav HTML cached once as `"nav-v2"` | +| `src/.../main.ts` | `initNavV2(nav)` when `[data-nav-v2]` present | + +--- + +## navigation-v2.yml wiring status + +| Section | Status | +|---------|--------| +| Elasticsearch fundamentals | Placeholders | +| Install, deploy, and administer | Placeholders | +| The Elasticsearch Platform → Ingest and manage data | Partially wired — "Ingest or migrate" group real pages + tocs; rest placeholders | +| The Elasticsearch Platform → Search, visualize and analyze | Placeholders | +| The Elasticsearch Platform → AI and machine learning | Placeholders | +| Solutions and project types | Placeholders | +| Reference | Commented out (18 real `toc:` entries ready, hidden while team reviews IA) | +| Troubleshooting | Commented out (real `page:` entries ready, hidden while team reviews IA) | + +### "Ingest or migrate" detail + +- Explicit `page:` entries for all current pages in `manage-data/ingest` and `manage-data/migrate` +- "Migrating your Elasticsearch data" uses `group:` + `page:` (page-folder): parent links to `manage-data/migrate`, four child pages beneath +- "Ingest tools (from Reference)" group: wired with 7 `toc:` entries (fleet, EDOT, integration-docs, search-connectors, logstash, apm, beats) + "Other ingest tools" sub-group (elasticsearch-hadoop, elastic-serverless-forwarder) +- `integration-docs` is `private: true` — absent in PR preview, present in production and localhost + +--- + +## Known limitations + +- **Private repos in PR preview**: `integration-docs` (Elastic integrations) is absent from preview builds; appears correctly in production and localhost. Same limitation affects V1. +- **URL paths**: V2 cannot assign new URL prefixes without wiring `NavigationTocMappings` from the V2 file (architectural blocker, documented in PR). + +--- + +## Open items + +1. **Remaining placeholders** — most sections still use `title:` placeholders pending IA decisions. Each now generates a greyed-but-clickable stub page at `/_placeholder/{hash}/`. +2. **Production migration** — wire `NavigationTocMappings` from V2 file; optional `path:` key on `toc:` entries for zero-redirect URL pinning. +3. **Re-enable Reference / Troubleshooting** — currently commented out; uncomment once team approves structure. + +--- + +## How to test locally + +```bash +dotnet run --no-restore --project src/tooling/docs-builder -- assembler build +dotnet run --project src/tooling/docs-builder -- assembler serve +# open http://localhost:4000 +``` diff --git a/src/Elastic.ApiExplorer/Layout/_ApiToc.cshtml b/src/Elastic.ApiExplorer/Layout/_ApiToc.cshtml index 3f6aa6e7fb..9996eeef2e 100644 --- a/src/Elastic.ApiExplorer/Layout/_ApiToc.cshtml +++ b/src/Elastic.ApiExplorer/Layout/_ApiToc.cshtml @@ -11,8 +11,8 @@
    @foreach (var item in Model) { -
  • - + @item.Heading diff --git a/src/Elastic.Codex/Page/Index.cshtml b/src/Elastic.Codex/Page/Index.cshtml index a7f1f08d7e..af56abd0b0 100644 --- a/src/Elastic.Codex/Page/Index.cshtml +++ b/src/Elastic.Codex/Page/Index.cshtml @@ -49,6 +49,8 @@ Previous = Model.PreviousDocument, Next = Model.NextDocument, NavigationHtml = Model.NavigationHtml, + NavV2Sections = Model.NavV2Sections, + ActiveSectionId = Model.ActiveSectionId, UrlPathPrefix = Model.UrlPathPrefix, GithubEditUrl = Model.GithubEditUrl, MarkdownUrl = Model.MarkdownUrl, diff --git a/src/Elastic.Documentation.Configuration/Builder/FeatureFlags.cs b/src/Elastic.Documentation.Configuration/Builder/FeatureFlags.cs index 73ca47910f..c1ab3cbf74 100644 --- a/src/Elastic.Documentation.Configuration/Builder/FeatureFlags.cs +++ b/src/Elastic.Documentation.Configuration/Builder/FeatureFlags.cs @@ -50,6 +50,12 @@ public bool DiagnosticsPanelEnabled set => _featureFlags["diagnostics-panel"] = value; } + public bool NavV2Enabled + { + get => IsEnabled("nav-v2"); + set => _featureFlags["nav-v2"] = value; + } + private bool IsEnabled(string key) { var envKey = $"FEATURE_{key.ToUpperInvariant().Replace('-', '_')}"; diff --git a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs index 45c7b676b5..3fca252636 100644 --- a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs +++ b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs @@ -31,6 +31,11 @@ public partial class ConfigurationFileProvider .WithTypeConverter(new ApiConfigurationConverter()) .Build(); + public static IDeserializer NavV2Deserializer { get; } = new StaticDeserializerBuilder(new YamlStaticContext()) + .WithNamingConvention(UnderscoredNamingConvention.Instance) + .WithTypeConverter(new NavV2FileYamlConverter()) + .Build(); + public ConfigurationSource ConfigurationSource { get; } public string? GitReference { get; } @@ -103,9 +108,11 @@ public ConfigurationFileProvider( ProductsFile = CreateTemporaryConfigurationFile("products.yml"); AssemblerFile = CreateTemporaryConfigurationFile("assembler.yml"); NavigationFile = CreateTemporaryConfigurationFile("navigation.yml"); + NavigationV2File = TryCreateTemporaryConfigurationFile("navigation-v2.yml"); LegacyUrlMappingsFile = CreateTemporaryConfigurationFile("legacy-url-mappings.yml"); // reading from synonyms.yml is temporary. If you spot this again as a future reader, feel free to remove it. SearchFile = CreateTemporaryConfigurationFile("search.yml", "synonyms.yml"); + WhatsNewFile = TryCreateTemporaryConfigurationFile("whats-new.yml"); } public bool SkipPrivateRepositories { get; } @@ -114,10 +121,16 @@ public ConfigurationFileProvider( public IFileInfo NavigationFile { get; private set; } + /// Optional — present only when config/navigation-v2.yml exists. + public IFileInfo? NavigationV2File { get; } + public IFileInfo VersionFile { get; } public IFileInfo ProductsFile { get; } + /// Optional — present only when config/whats-new.yml exists. + public IFileInfo? WhatsNewFile { get; } + public IFileInfo AssemblerFile { get; } public IFileInfo LegacyUrlMappingsFile { get; } @@ -198,6 +211,31 @@ public IFileInfo CreateNavigationFile(AssemblyConfiguration configuration) } + private IFileInfo? TryCreateTemporaryConfigurationFile(string fileName) + { + if (ConfigurationSource == ConfigurationSource.Local) + { + var localPath = GetLocalPath(fileName); + if (!_fileSystem.File.Exists(localPath)) + return null; + } + else if (ConfigurationSource == ConfigurationSource.Remote) + { + var appDataPath = GetAppDataPath(fileName); + if (!_fileSystem.File.Exists(appDataPath)) + return null; + } + else + { + // Embedded — check if resource exists + var resourceName = $"{_assemblyName}.{fileName}"; + if (typeof(ConfigurationFileProvider).Assembly.GetManifestResourceStream(resourceName) is null) + return null; + } + + return CreateTemporaryConfigurationFile(fileName); + } + private IFileInfo CreateTemporaryConfigurationFile(string fileName, string? fallback = null) { using var stream = GetLocalOrEmbedded(fileName, fallback); diff --git a/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj b/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj index 8796dc5fd7..946b24e1fe 100644 --- a/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj +++ b/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj @@ -34,5 +34,6 @@ + diff --git a/src/Elastic.Documentation.Configuration/Products/Product.cs b/src/Elastic.Documentation.Configuration/Products/Product.cs index a1f6366468..3f4e851551 100644 --- a/src/Elastic.Documentation.Configuration/Products/Product.cs +++ b/src/Elastic.Documentation.Configuration/Products/Product.cs @@ -84,5 +84,12 @@ public record Product public VersioningSystem? VersioningSystem { get; init; } public string? Repository { get; init; } public ProductFeatures Features { get; init; } = ProductFeatures.All; + + /// + /// Slug of the product hub page, relative to the site root (no leading or trailing slash). + /// When set, regular pages that declare this product in frontmatter render a clickable + /// badge linking to /{Hub}/. When null, no badge is rendered for the product. + /// + public string? Hub { get; init; } } diff --git a/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs b/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs index 0c825afb2d..3e12fa8498 100644 --- a/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs +++ b/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs @@ -34,7 +34,8 @@ public static ProductsConfiguration CreateProducts(this ConfigurationFileProvide DisplayName = kvp.Value.Display, VersioningSystem = versioningSystem, Repository = kvp.Value.Repository ?? kvp.Key, - Features = features + Features = features, + Hub = kvp.Value.Hub }; }); @@ -103,4 +104,7 @@ internal sealed record ProductDto [YamlMember(Alias = "features")] public Dictionary? Features { get; set; } + + [YamlMember(Alias = "hub")] + public string? Hub { get; set; } } diff --git a/src/Elastic.Documentation.Configuration/Toc/NavigationV2File.cs b/src/Elastic.Documentation.Configuration/Toc/NavigationV2File.cs new file mode 100644 index 0000000000..ab85ff9cdd --- /dev/null +++ b/src/Elastic.Documentation.Configuration/Toc/NavigationV2File.cs @@ -0,0 +1,217 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Elastic.Documentation.Configuration.Toc; + +public interface INavV2Item { } + +public record LabelNavV2Item( + string Label, + bool Expanded, + IReadOnlyList Children +) : INavV2Item; + +public record TocNavV2Item( + Uri Source, + IReadOnlyList Children +) : INavV2Item; + +/// +/// Represents a single-page crosslink (when is non-null) or +/// a placeholder disabled link (when is null and only is set). +/// +public record PageNavV2Item(Uri? Page, string? Title) : INavV2Item; + +/// +/// A top-level section that owns an independent sidebar tree and (optionally) a tab in the +/// secondary nav bar. When is true the section does not appear +/// in the top bar and renders with a back-arrow instead. +/// +public record SectionNavV2Item( + string Label, + string Url, + bool Isolated, + bool Dropdown, + IReadOnlyList Children +) : INavV2Item; + +/// +/// A nav island nested inside a section. When a user navigates into pages belonging to +/// this island's toc, the sidebar shows only the island's tree with a back arrow to the +/// parent section. The island does not appear in the secondary nav bar. +/// +public record IslandNavV2Item( + string Label, + Uri Source +) : INavV2Item; + +/// +/// A folder node — has a title and children, with an optional page: URI. +/// When is set, the header is a real clickable link; otherwise it renders +/// as a disabled placeholder (cursor-not-allowed). +/// +public record GroupNavV2Item( + string Title, + Uri? Page, + IReadOnlyList Children +) : INavV2Item; + +public class NavigationV2File +{ + [YamlMember(Alias = "nav")] + public IReadOnlyList Nav { get; set; } = []; + + public static NavigationV2File Deserialize(string yaml) => + ConfigurationFileProvider.NavV2Deserializer.Deserialize(yaml); +} + +public class NavV2FileYamlConverter : IYamlTypeConverter +{ + public bool Accepts(Type type) => type == typeof(NavigationV2File) || type == typeof(INavV2Item); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + if (type == typeof(NavigationV2File)) + return ReadFile(parser, rootDeserializer); + return ReadItem(parser, rootDeserializer); + } + + private static NavigationV2File ReadFile(IParser parser, ObjectDeserializer rootDeserializer) + { + if (!parser.TryConsume(out _)) + return new NavigationV2File(); + + var file = new NavigationV2File(); + while (!parser.TryConsume(out _)) + { + var key = parser.Consume(); + if (key.Value == "nav") + file.Nav = ReadItemList(parser, rootDeserializer); + else + parser.SkipThisAndNestedEvents(); + } + return file; + } + + private static IReadOnlyList ReadItemList(IParser parser, ObjectDeserializer rootDeserializer) + { + var items = new List(); + if (!parser.TryConsume(out _)) + return items; + + while (!parser.TryConsume(out _)) + { + var item = ReadItem(parser, rootDeserializer); + if (item is not null) + items.Add(item); + } + return items; + } + + private static INavV2Item? ReadItem(IParser parser, ObjectDeserializer rootDeserializer) + { + if (!parser.TryConsume(out _)) + return null; + + var dict = new Dictionary(); + while (!parser.TryConsume(out _)) + { + var key = parser.Consume(); + if (key.Value == "children") + dict["children"] = ReadItemList(parser, rootDeserializer); + else if (parser.Accept(out var val)) + { + dict[key.Value] = val.Value; + _ = parser.MoveNext(); + } + else + parser.SkipThisAndNestedEvents(); + } + + if (dict.TryGetValue("section", out var sectionVal) && sectionVal is string sectionStr) + { + var sectionUrl = dict.TryGetValue("url", out var suVal) && suVal is string suStr ? suStr : "/"; + var isolated = dict.TryGetValue("isolated", out var isoVal) + && isoVal is string isoStr + && bool.TryParse(isoStr, out var isoBool) + && isoBool; + var dropdown = dict.TryGetValue("dropdown", out var ddVal) + && ddVal is string ddStr + && bool.TryParse(ddStr, out var ddBool) + && ddBool; + var sectionChildren = dict.TryGetValue("children", out var sch) && sch is IReadOnlyList sChildList + ? sChildList + : []; + return new SectionNavV2Item(sectionStr, sectionUrl, isolated, dropdown, sectionChildren); + } + + if (dict.TryGetValue("island", out var islandVal) && islandVal is string islandStr) + { + if (dict.TryGetValue("toc", out var itVal) && itVal is string itStr) + { + var itUriString = itStr.Contains("://") ? itStr : $"docs-content://{itStr}"; + if (Uri.TryCreate(itUriString, UriKind.Absolute, out var itSource)) + return new IslandNavV2Item(islandStr, itSource); + } + return null; + } + + if (dict.TryGetValue("label", out var labelVal) && labelVal is string labelStr) + { + var expanded = dict.TryGetValue("expanded", out var expVal) + && expVal is string expStr + && bool.TryParse(expStr, out var expBool) + && expBool; + var labelChildren = dict.TryGetValue("children", out var lch) && lch is IReadOnlyList lChildList + ? lChildList + : []; + return new LabelNavV2Item(labelStr, expanded, labelChildren); + } + + if (dict.TryGetValue("toc", out var tocVal) && tocVal is string tocStr) + { + var uriString = tocStr.Contains("://") ? tocStr : $"docs-content://{tocStr}"; + if (!Uri.TryCreate(uriString, UriKind.Absolute, out var source)) + throw new InvalidOperationException($"Invalid TOC source: '{tocStr}'"); + var tocChildren = dict.TryGetValue("children", out var tch) && tch is IReadOnlyList tChildList + ? tChildList + : []; + return new TocNavV2Item(source, tocChildren); + } + + if (dict.TryGetValue("group", out var groupVal) && groupVal is string groupStr) + { + var groupChildren = dict.TryGetValue("children", out var gch) && gch is IReadOnlyList gChildList + ? gChildList + : []; + Uri? groupPage = null; + if (dict.TryGetValue("page", out var gpVal) && gpVal is string gpStr) + { + var gpUri = gpStr.Contains("://") ? gpStr : $"docs-content://{gpStr}"; + _ = Uri.TryCreate(gpUri, UriKind.Absolute, out groupPage); + } + return new GroupNavV2Item(groupStr, groupPage, groupChildren); + } + + if (dict.TryGetValue("page", out var pageVal) && pageVal is string pageStr) + { + var uriString = pageStr.Contains("://") ? pageStr : $"docs-content://{pageStr}"; + _ = Uri.TryCreate(uriString, UriKind.Absolute, out var pageUri); + var title = dict.TryGetValue("title", out var t) && t is string ts ? ts : null; + return new PageNavV2Item(pageUri, title); + } + + if (dict.TryGetValue("title", out var titleVal) && titleVal is string titleStr) + return new PageNavV2Item(null, titleStr); + + return null; + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) => + serializer.Invoke(value, type); +} diff --git a/src/Elastic.Documentation.Links/CrossLinks/CrossLinkFetcher.cs b/src/Elastic.Documentation.Links/CrossLinks/CrossLinkFetcher.cs index 2c54f1d374..8a3ead529f 100644 --- a/src/Elastic.Documentation.Links/CrossLinks/CrossLinkFetcher.cs +++ b/src/Elastic.Documentation.Links/CrossLinks/CrossLinkFetcher.cs @@ -114,6 +114,13 @@ protected async Task FetchCrossLinks(string repository, string[ return await FetchLinkIndexEntry(repository, linkIndexEntry, ctx); } + // Branch not published yet (e.g. a prototype/dev branch) — fall back to main or master. + foreach (var fallback in new[] { "main", "master" }) + { + if (repositoryLinks.TryGetValue(fallback, out var fallbackEntry)) + return await FetchLinkIndexEntry(repository, fallbackEntry, ctx); + } + throw new Exception($"Repository found in link index however none of: '{string.Join(", ", keys)}' branches found"); } diff --git a/src/Elastic.Documentation.Navigation/Assembler/SiteNavigation.cs b/src/Elastic.Documentation.Navigation/Assembler/SiteNavigation.cs index 291c80bed3..0e59fb4cd6 100644 --- a/src/Elastic.Documentation.Navigation/Assembler/SiteNavigation.cs +++ b/src/Elastic.Documentation.Navigation/Assembler/SiteNavigation.cs @@ -104,6 +104,11 @@ public SiteNavigation( // Build positional navigation lookup tables from all navigation items in a single traversal NavigationDocumentationFileLookup = []; NavigationIndexedByOrder = this.BuildNavigationLookups(NavigationDocumentationFileLookup); + foreach (var node in UnseenNodes) + { + if (_nodes.TryGetValue(node, out var value)) + value.AddNavigationFileLookups(NavigationDocumentationFileLookup); + } } public HashSet DeclaredPhantoms { get; } diff --git a/src/Elastic.Documentation.Navigation/NavigationItemExtensions.cs b/src/Elastic.Documentation.Navigation/NavigationItemExtensions.cs index dbdd327796..c851904805 100644 --- a/src/Elastic.Documentation.Navigation/NavigationItemExtensions.cs +++ b/src/Elastic.Documentation.Navigation/NavigationItemExtensions.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using Elastic.Documentation.Navigation.Isolated; using Elastic.Documentation.Navigation.Isolated.Leaf; +using Elastic.Documentation.Navigation.V2; namespace Elastic.Documentation.Navigation; @@ -87,17 +88,27 @@ public static FrozenDictionary BuildNavigationLookups( ) { var navigationByOrder = new Dictionary(); - BuildNavigationLookupsRecursive(rootItem, navigationDocumentationFileLookup, navigationByOrder); + var urlToFile = new Dictionary(StringComparer.OrdinalIgnoreCase); + BuildNavigationLookupsRecursive(rootItem, navigationDocumentationFileLookup, navigationByOrder, urlToFile); + ResolvePageCrossLinksRecursive(rootItem, navigationDocumentationFileLookup, urlToFile); return navigationByOrder.ToFrozenDictionary(); } + public static void AddNavigationFileLookups( + this INavigationItem rootItem, + ConditionalWeakTable navigationDocumentationFileLookup + ) => + AddNavigationFileLookupsRecursive(rootItem, navigationDocumentationFileLookup); + /// - /// Recursively builds both NavigationDocumentationFileLookup and NavigationIndexedByOrder in a single traversal + /// Recursively builds both NavigationDocumentationFileLookup and NavigationIndexedByOrder in a single traversal, + /// also collecting a URL-to-file map used by the second pass to resolve V2 page: entries. /// private static void BuildNavigationLookupsRecursive( INavigationItem item, ConditionalWeakTable navigationDocumentationFileLookup, - Dictionary navigationByOrder) + Dictionary navigationByOrder, + Dictionary urlToFile) { switch (item) { @@ -107,6 +118,7 @@ private static void BuildNavigationLookupsRecursive( case ILeafNavigationItem documentationFileLeaf: _ = navigationDocumentationFileLookup.TryAdd(documentationFileLeaf.Model, documentationFileLeaf); _ = navigationByOrder.TryAdd(documentationFileLeaf.NavigationIndex, documentationFileLeaf); + _ = urlToFile.TryAdd(documentationFileLeaf.Url, documentationFileLeaf.Model); break; case ILeafNavigationItem leaf: _ = navigationByOrder.TryAdd(leaf.NavigationIndex, leaf); @@ -115,13 +127,61 @@ private static void BuildNavigationLookupsRecursive( _ = navigationDocumentationFileLookup.TryAdd(documentationFileNode.Index.Model, documentationFileNode); _ = navigationByOrder.TryAdd(documentationFileNode.NavigationIndex, documentationFileNode); _ = navigationByOrder.TryAdd(documentationFileNode.Index.NavigationIndex, documentationFileNode.Index); + _ = urlToFile.TryAdd(documentationFileNode.Url, documentationFileNode.Index.Model); + _ = urlToFile.TryAdd(documentationFileNode.Index.Url, documentationFileNode.Index.Model); foreach (var child in documentationFileNode.NavigationItems) - BuildNavigationLookupsRecursive(child, navigationDocumentationFileLookup, navigationByOrder); + BuildNavigationLookupsRecursive(child, navigationDocumentationFileLookup, navigationByOrder, urlToFile); break; case INodeNavigationItem node: _ = navigationByOrder.TryAdd(node.NavigationIndex, node); foreach (var child in node.NavigationItems) - BuildNavigationLookupsRecursive(child, navigationDocumentationFileLookup, navigationByOrder); + BuildNavigationLookupsRecursive(child, navigationDocumentationFileLookup, navigationByOrder, urlToFile); + break; + } + } + + private static void AddNavigationFileLookupsRecursive( + INavigationItem item, + ConditionalWeakTable navigationDocumentationFileLookup) + { + switch (item) + { + case CrossLinkNavigationLeaf: + break; + case ILeafNavigationItem documentationFileLeaf: + _ = navigationDocumentationFileLookup.TryAdd(documentationFileLeaf.Model, documentationFileLeaf); + break; + case INodeNavigationItem documentationFileNode: + _ = navigationDocumentationFileLookup.TryAdd(documentationFileNode.Index.Model, documentationFileNode); + foreach (var child in documentationFileNode.NavigationItems) + AddNavigationFileLookupsRecursive(child, navigationDocumentationFileLookup); + break; + case INodeNavigationItem node: + foreach (var child in node.NavigationItems) + AddNavigationFileLookupsRecursive(child, navigationDocumentationFileLookup); + break; + } + } + + /// + /// Second pass: for every V2 page: entry () whose URL + /// matches a file already known from the V1 toc traversal, register that file in the lookup + /// so HtmlWriter.RenderLayout can position it correctly without a V1 nav backing. + /// + private static void ResolvePageCrossLinksRecursive( + INavigationItem item, + ConditionalWeakTable navigationDocumentationFileLookup, + Dictionary urlToFile) + { + switch (item) + { + case PageCrossLinkLeaf pageCrossLink: + if (urlToFile.TryGetValue(pageCrossLink.Url, out var file)) + _ = navigationDocumentationFileLookup.TryAdd(file, pageCrossLink); + break; + case INodeNavigationItem node: + foreach (var child in node.NavigationItems) + ResolvePageCrossLinksRecursive(child, navigationDocumentationFileLookup, urlToFile); break; } } diff --git a/src/Elastic.Documentation.Navigation/V2/IslandNavigationNode.cs b/src/Elastic.Documentation.Navigation/V2/IslandNavigationNode.cs new file mode 100644 index 0000000000..619aceac7f --- /dev/null +++ b/src/Elastic.Documentation.Navigation/V2/IslandNavigationNode.cs @@ -0,0 +1,49 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Documentation.Extensions; + +namespace Elastic.Documentation.Navigation.V2; + +/// +/// A nav island that wraps an existing toc node. When a page belongs to this island, +/// the sidebar shows only the island's tree with a back arrow to the parent section. +/// In the parent section's sidebar, the island renders as a normal folder link. +/// +public class IslandNavigationNode( + string label, + IRootNavigationItem source, + INodeNavigationItem? parent +) : INodeNavigationItem +{ + /// The Id of the wrapped toc root, used for island lookup by nav ownership. + public string SourceTocRootId { get; } = source.Id; + + /// + public string Id { get; } = ShortId.Create("island", label); + + /// + public string Url => source.Url; + + /// + public string NavigationTitle { get; } = label; + + /// + public IRootNavigationItem NavigationRoot { get; } = parent?.NavigationRoot!; + + /// + public INodeNavigationItem? Parent { get; set; } = parent; + + /// + public bool Hidden => source.Hidden; + + /// + public int NavigationIndex { get; set; } + + /// + public ILeafNavigationItem Index => source.Index; + + /// + public IReadOnlyCollection NavigationItems => source.NavigationItems; +} diff --git a/src/Elastic.Documentation.Navigation/V2/LabelNavigationNode.cs b/src/Elastic.Documentation.Navigation/V2/LabelNavigationNode.cs new file mode 100644 index 0000000000..5588fd3206 --- /dev/null +++ b/src/Elastic.Documentation.Navigation/V2/LabelNavigationNode.cs @@ -0,0 +1,74 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Documentation.Extensions; + +namespace Elastic.Documentation.Navigation.V2; + +/// +/// A non-clickable section heading in the V2 navigation sidebar. +/// Has children but no URL of its own. +/// +public class LabelNavigationNode : INodeNavigationItem +{ + private readonly LabelIndexLeaf _index; + + public LabelNavigationNode( + string label, + bool expandedByDefault, + IReadOnlyCollection children, + INodeNavigationItem? parent + ) + { + Id = ShortId.Create("label", label); + NavigationTitle = label; + ExpandedByDefault = expandedByDefault; + NavigationItems = children; + Parent = parent; + NavigationRoot = parent?.NavigationRoot!; + _index = new LabelIndexLeaf(this); + } + + /// Whether this label section starts expanded by default. + public bool ExpandedByDefault { get; } + + /// + public string Id { get; } + + /// + public string Url => string.Empty; + + /// + public string NavigationTitle { get; } + + /// + public IRootNavigationItem NavigationRoot { get; } + + /// + public INodeNavigationItem? Parent { get; set; } + + /// + public bool Hidden => false; + + /// + public int NavigationIndex { get; set; } + + /// + public ILeafNavigationItem Index => _index; + + /// + public IReadOnlyCollection NavigationItems { get; } + + private sealed class LabelIndexLeaf(LabelNavigationNode owner) + : ILeafNavigationItem, INavigationModel + { + public INavigationModel Model => this; + public string Url => string.Empty; + public string NavigationTitle => owner.NavigationTitle; + public IRootNavigationItem NavigationRoot => owner.NavigationRoot; + public INodeNavigationItem? Parent { get; set; } = owner; + public bool Hidden => true; + public int NavigationIndex { get; set; } + } +} diff --git a/src/Elastic.Documentation.Navigation/V2/NavigationSection.cs b/src/Elastic.Documentation.Navigation/V2/NavigationSection.cs new file mode 100644 index 0000000000..467d61db13 --- /dev/null +++ b/src/Elastic.Documentation.Navigation/V2/NavigationSection.cs @@ -0,0 +1,31 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 + +namespace Elastic.Documentation.Navigation.V2; + +/// +/// Lightweight data carrier for a navigation section, used by the rendering layer +/// to drive the secondary nav bar tabs and resolve which sidebar to show. +/// +public record NavigationSection( + string Id, + string Label, + string Url, + bool Isolated, + bool Dropdown, + IReadOnlyList NavigationItems +); + +/// +/// A nav island nested within a parent section. When a page belongs to an island, +/// the sidebar shows only the island's tree with a back arrow to the parent section. +/// +public record NavigationIsland( + string Id, + string Label, + string Url, + string SourceTocRootId, + NavigationSection ParentSection, + IReadOnlyList NavigationItems +); diff --git a/src/Elastic.Documentation.Navigation/V2/PageCrossLinkLeaf.cs b/src/Elastic.Documentation.Navigation/V2/PageCrossLinkLeaf.cs new file mode 100644 index 0000000000..51927aaf14 --- /dev/null +++ b/src/Elastic.Documentation.Navigation/V2/PageCrossLinkLeaf.cs @@ -0,0 +1,53 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 + +namespace Elastic.Documentation.Navigation.V2; + +/// +/// A real cross-link leaf in the V2 sidebar — has a URL derived from the source +/// page: URI and the site prefix. Renders as a normal clickable link. +/// +public class PageCrossLinkLeaf( + Uri page, + string title, + string sitePrefix, + INodeNavigationItem? parent +) : ILeafNavigationItem, INavigationModel +{ + public Uri Page { get; } = page; + + /// + public INavigationModel Model => this; + + /// + public string Url { get; } = ResolveUrl(page, sitePrefix); + + /// + public string NavigationTitle { get; } = title; + + /// + public IRootNavigationItem NavigationRoot { get; } = parent?.NavigationRoot!; + + /// + public INodeNavigationItem? Parent { get; set; } = parent; + + /// + public bool Hidden => false; + + /// + public int NavigationIndex { get; set; } + + private static string ResolveUrl(Uri page, string sitePrefix) + { + // URI host carries the first path segment (e.g. docs-content://manage-data/ingest.md + // → host="manage-data", absolutePath="/ingest.md"), so combine both. + var path = (page.Host + page.AbsolutePath).Trim('/'); + if (path.EndsWith(".md", StringComparison.OrdinalIgnoreCase)) + path = path[..^3]; + if (path.EndsWith("/index", StringComparison.OrdinalIgnoreCase)) + path = path[..^6]; + var prefix = "/" + sitePrefix.Trim('/'); + return string.IsNullOrEmpty(path) ? prefix : $"{prefix}/{path}"; + } +} diff --git a/src/Elastic.Documentation.Navigation/V2/PageFolderNavigationNode.cs b/src/Elastic.Documentation.Navigation/V2/PageFolderNavigationNode.cs new file mode 100644 index 0000000000..b19b2994b4 --- /dev/null +++ b/src/Elastic.Documentation.Navigation/V2/PageFolderNavigationNode.cs @@ -0,0 +1,87 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Documentation.Extensions; + +namespace Elastic.Documentation.Navigation.V2; + +/// +/// A folder node in the V2 navigation sidebar that also carries a real URL. +/// Used when a group: entry in navigation-v2.yml has an associated page: key. +/// Renders as an expandable section whose header is a clickable link. +/// +public class PageFolderNavigationNode : INodeNavigationItem +{ + private readonly PageFolderIndexLeaf _index; + + public PageFolderNavigationNode( + string title, + Uri page, + string sitePrefix, + IReadOnlyCollection children, + INodeNavigationItem? parent + ) + { + Id = ShortId.Create("page-folder", title); + NavigationTitle = title; + Page = page; + Url = ResolveUrl(page, sitePrefix); + NavigationItems = children; + Parent = parent; + NavigationRoot = parent?.NavigationRoot!; + _index = new PageFolderIndexLeaf(this); + } + + /// + public string Id { get; } + + public Uri Page { get; } + + /// + public string Url { get; } + + /// + public string NavigationTitle { get; } + + /// + public IRootNavigationItem NavigationRoot { get; } + + /// + public INodeNavigationItem? Parent { get; set; } + + /// + public bool Hidden => false; + + /// + public int NavigationIndex { get; set; } + + /// + public ILeafNavigationItem Index => _index; + + /// + public IReadOnlyCollection NavigationItems { get; } + + private static string ResolveUrl(Uri page, string sitePrefix) + { + var path = (page.Host + page.AbsolutePath).Trim('/'); + if (path.EndsWith(".md", StringComparison.OrdinalIgnoreCase)) + path = path[..^3]; + if (path.EndsWith("/index", StringComparison.OrdinalIgnoreCase)) + path = path[..^6]; + var prefix = "/" + sitePrefix.Trim('/'); + return string.IsNullOrEmpty(path) ? prefix : $"{prefix}/{path}"; + } + + private sealed class PageFolderIndexLeaf(PageFolderNavigationNode owner) + : ILeafNavigationItem, INavigationModel + { + public INavigationModel Model => this; + public string Url => owner.Url; + public string NavigationTitle => owner.NavigationTitle; + public IRootNavigationItem NavigationRoot => owner.NavigationRoot; + public INodeNavigationItem? Parent { get; set; } = owner; + public bool Hidden => true; + public int NavigationIndex { get; set; } + } +} diff --git a/src/Elastic.Documentation.Navigation/V2/PlaceholderNavigationLeaf.cs b/src/Elastic.Documentation.Navigation/V2/PlaceholderNavigationLeaf.cs new file mode 100644 index 0000000000..e1213a7316 --- /dev/null +++ b/src/Elastic.Documentation.Navigation/V2/PlaceholderNavigationLeaf.cs @@ -0,0 +1,47 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Documentation.Extensions; + +namespace Elastic.Documentation.Navigation.V2; + +/// +/// A placeholder link in the V2 navigation sidebar. +/// Has a URL pointing to a generated stub page but is rendered greyed-out +/// to indicate the content is not yet finalised. +/// +public class PlaceholderNavigationLeaf( + string title, + string sitePrefix, + INodeNavigationItem? parent +) : ILeafNavigationItem, INavigationModel +{ + /// + public INavigationModel Model => this; + + /// + public string Url { get; } = ComputeUrl(title, sitePrefix); + + /// + public string NavigationTitle { get; } = title; + + /// + public IRootNavigationItem NavigationRoot { get; } = parent?.NavigationRoot!; + + /// + public INodeNavigationItem? Parent { get; set; } = parent; + + /// + public bool Hidden => false; + + /// + public int NavigationIndex { get; set; } + + private static string ComputeUrl(string title, string sitePrefix) + { + var hash = ShortId.Create("placeholder", title); + var prefix = string.IsNullOrEmpty(sitePrefix) ? "" : "/" + sitePrefix.Trim('/'); + return $"{prefix}/_placeholder/{hash}"; + } +} diff --git a/src/Elastic.Documentation.Navigation/V2/PlaceholderNavigationNode.cs b/src/Elastic.Documentation.Navigation/V2/PlaceholderNavigationNode.cs new file mode 100644 index 0000000000..5835f191ae --- /dev/null +++ b/src/Elastic.Documentation.Navigation/V2/PlaceholderNavigationNode.cs @@ -0,0 +1,79 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Documentation.Extensions; + +namespace Elastic.Documentation.Navigation.V2; + +/// +/// A placeholder folder node in the V2 navigation sidebar. +/// Has a URL pointing to a generated stub page; rendered greyed-out +/// to indicate the content is not yet finalised. +/// +public class PlaceholderNavigationNode : INodeNavigationItem +{ + private readonly PlaceholderIndexLeaf _index; + + public PlaceholderNavigationNode( + string title, + string sitePrefix, + IReadOnlyCollection children, + INodeNavigationItem? parent + ) + { + Id = ShortId.Create("placeholder-group", title); + NavigationTitle = title; + Url = ComputeUrl(title, sitePrefix); + NavigationItems = children; + Parent = parent; + NavigationRoot = parent?.NavigationRoot!; + _index = new PlaceholderIndexLeaf(this); + } + + /// + public string Id { get; } + + /// + public string Url { get; } + + /// + public string NavigationTitle { get; } + + /// + public IRootNavigationItem NavigationRoot { get; } + + /// + public INodeNavigationItem? Parent { get; set; } + + /// + public bool Hidden => false; + + /// + public int NavigationIndex { get; set; } + + /// + public ILeafNavigationItem Index => _index; + + /// + public IReadOnlyCollection NavigationItems { get; } + + private static string ComputeUrl(string title, string sitePrefix) + { + var hash = ShortId.Create("placeholder", title); + var prefix = string.IsNullOrEmpty(sitePrefix) ? "" : "/" + sitePrefix.Trim('/'); + return $"{prefix}/_placeholder/{hash}"; + } + + private sealed class PlaceholderIndexLeaf(PlaceholderNavigationNode owner) + : ILeafNavigationItem, INavigationModel + { + public INavigationModel Model => this; + public string Url => owner.Url; + public string NavigationTitle => owner.NavigationTitle; + public IRootNavigationItem NavigationRoot => owner.NavigationRoot; + public INodeNavigationItem? Parent { get; set; } = owner; + public bool Hidden => true; + public int NavigationIndex { get; set; } + } +} diff --git a/src/Elastic.Documentation.Navigation/V2/SectionNavigationNode.cs b/src/Elastic.Documentation.Navigation/V2/SectionNavigationNode.cs new file mode 100644 index 0000000000..eb6f37ec2d --- /dev/null +++ b/src/Elastic.Documentation.Navigation/V2/SectionNavigationNode.cs @@ -0,0 +1,82 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Documentation.Extensions; + +namespace Elastic.Documentation.Navigation.V2; + +/// +/// A top-level section that owns an independent sidebar nav tree. +/// Unlike , a section has a real URL (the tab link) +/// and an flag that controls whether it appears in the top bar. +/// +public class SectionNavigationNode : INodeNavigationItem +{ + private readonly SectionIndexLeaf _index; + + public SectionNavigationNode( + string label, + string url, + bool isolated, + bool dropdown, + IReadOnlyCollection children, + INodeNavigationItem? parent + ) + { + Id = ShortId.Create("section", label); + NavigationTitle = label; + Url = url; + Isolated = isolated; + Dropdown = dropdown; + NavigationItems = children; + Parent = parent; + NavigationRoot = parent?.NavigationRoot!; + _index = new SectionIndexLeaf(this); + } + + /// Whether this section is excluded from the top bar and renders with a back arrow. + public bool Isolated { get; } + + /// Whether the top-bar entry should render as a dropdown listing the section's children. + public bool Dropdown { get; } + + /// + public string Id { get; } + + /// + public string Url { get; } + + /// + public string NavigationTitle { get; } + + /// + public IRootNavigationItem NavigationRoot { get; } + + /// + public INodeNavigationItem? Parent { get; set; } + + /// + public bool Hidden => false; + + /// + public int NavigationIndex { get; set; } + + /// + public ILeafNavigationItem Index => _index; + + /// + public IReadOnlyCollection NavigationItems { get; } + + private sealed class SectionIndexLeaf(SectionNavigationNode owner) + : ILeafNavigationItem, INavigationModel + { + public INavigationModel Model => this; + public string Url => owner.Url; + public string NavigationTitle => owner.NavigationTitle; + public IRootNavigationItem NavigationRoot => owner.NavigationRoot; + public INodeNavigationItem? Parent { get; set; } = owner; + public bool Hidden => true; + public int NavigationIndex { get; set; } + } +} diff --git a/src/Elastic.Documentation.Navigation/V2/SiteNavigationV2.cs b/src/Elastic.Documentation.Navigation/V2/SiteNavigationV2.cs new file mode 100644 index 0000000000..c07e24a304 --- /dev/null +++ b/src/Elastic.Documentation.Navigation/V2/SiteNavigationV2.cs @@ -0,0 +1,450 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.Toc; +using Elastic.Documentation.Navigation.Assembler; +using Elastic.Documentation.Navigation.Isolated.Node; + +namespace Elastic.Documentation.Navigation.V2; + +/// +/// Extends with a V2 section-structured sidebar derived from +/// navigation-v2.yml. Content is built at the same URL paths as V1 (the original +/// is passed to the base constructor unchanged). +/// +/// Top-level section: items become independent nav trees (). +/// Each section drives a tab in the secondary nav bar and its own sidebar. +/// Sections marked isolated: true do not appear in the top bar and render with a back arrow. +/// +/// +public class SiteNavigationV2 : SiteNavigation +{ + private readonly Dictionary _urlToSection = new(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary _tocRootToIsland = new(StringComparer.Ordinal); + + public SiteNavigationV2( + NavigationV2File v2File, + SiteNavigationFile originalFile, + IDocumentationContext context, + IReadOnlyCollection documentationSetNavigations, + string? sitePrefix + ) : base(originalFile, context, documentationSetNavigations, sitePrefix) + { + var prefix = sitePrefix ?? string.Empty; + V2NavigationItems = BuildV2Items(v2File.Nav, Nodes, this, prefix); + RegisterV2PageLookups(); + Sections = BuildSections(V2NavigationItems); + Islands = BuildIslands(Sections); + BuildUrlToSectionLookup(); + BuildTocRootToIslandLookup(); + } + + /// + /// All V2 navigation items (flat list including sections, labels, etc.). + /// Used for placeholder generation and full-tree traversal. + /// + public IReadOnlyList V2NavigationItems { get; } + + /// + /// Top-level sections extracted from . + /// Each section owns an independent sidebar nav tree. + /// + public IReadOnlyList Sections { get; } + + /// + /// Nav islands nested within sections. When a page belongs to an island, + /// the sidebar shows the island's tree with a back arrow to the parent section. + /// + public IReadOnlyList Islands { get; } + + /// + /// Resolves which island a page belongs to by its toc root. + /// Uses the nav ownership model — the toc root that owns the page determines the island. + /// + public NavigationIsland? GetIslandForTocRoot(IRootNavigationItem tocRoot) => + _tocRootToIsland.GetValueOrDefault(tocRoot.Id); + + /// + /// Resolves which section a page belongs to by its URL. + /// Returns the first non-isolated section as fallback for unresolved URLs. + /// + public NavigationSection? GetSectionForUrl(string? pageUrl) + { + if (pageUrl is not null) + { + var normalized = pageUrl.TrimEnd('/'); + if (_urlToSection.TryGetValue(normalized, out var section)) + return section; + if (_urlToSection.TryGetValue(normalized + "/", out section)) + return section; + } + return Sections.FirstOrDefault(s => !s.Isolated); + } + + private static IReadOnlyList BuildSections(IReadOnlyList items) => + items + .OfType() + .Select(s => new NavigationSection(s.Id, s.NavigationTitle, s.Url, s.Isolated, s.Dropdown, [.. s.NavigationItems])) + .ToList(); + + private static IReadOnlyList BuildIslands(IReadOnlyList sections) + { + var islands = new List(); + foreach (var section in sections) + CollectIslandsFromItems(section.NavigationItems, section, islands); + return islands; + } + + private static void CollectIslandsFromItems( + IEnumerable items, + NavigationSection parentSection, + List islands + ) + { + foreach (var item in items) + { + if (item is IslandNavigationNode islandNode) + { + islands.Add(new NavigationIsland( + islandNode.Id, + islandNode.NavigationTitle, + islandNode.Url, + islandNode.SourceTocRootId, + parentSection, + [.. islandNode.NavigationItems] + )); + } + else if (item is INodeNavigationItem node) + { + CollectIslandsFromItems(node.NavigationItems, parentSection, islands); + } + } + } + + private void BuildUrlToSectionLookup() + { + foreach (var section in Sections) + CollectUrlsForSection(section.NavigationItems, section); + } + + private void CollectUrlsForSection(IEnumerable items, NavigationSection section) + { + foreach (var item in items) + { + // Skip island subtrees — they're handled by the island lookup + if (item is IslandNavigationNode) + continue; + + if (!string.IsNullOrEmpty(item.Url)) + { + var normalized = item.Url.TrimEnd('/'); + _ = _urlToSection.TryAdd(normalized, section); + } + + if (item is INodeNavigationItem node) + CollectUrlsForSection(node.NavigationItems, section); + } + } + + private void BuildTocRootToIslandLookup() + { + foreach (var island in Islands) + _ = _tocRootToIsland.TryAdd(island.SourceTocRootId, island); + } + + private void RegisterV2PageLookups() + { + var urlToFile = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var node in Nodes.Values) + CollectDocumentationFilesByUrl(node, urlToFile, Url); + + RegisterV2PageLookups(V2NavigationItems, urlToFile); + } + + private void RegisterV2PageLookups( + IEnumerable items, + IReadOnlyDictionary urlToFile + ) + { + foreach (var item in items) + { + switch (item) + { + case PageCrossLinkLeaf pageCrossLink: + RegisterV2PageLookup(pageCrossLink.Page, pageCrossLink, urlToFile); + break; + case PageFolderNavigationNode pageFolder: + RegisterV2PageLookup(pageFolder.Page, pageFolder, urlToFile); + break; + } + + if (item is INodeNavigationItem node) + RegisterV2PageLookups(node.NavigationItems, urlToFile); + } + } + + private void RegisterV2PageLookup( + Uri page, + INavigationItem item, + IReadOnlyDictionary urlToFile + ) + { + if (TryResolvePageSource(page, out var file) || urlToFile.TryGetValue(item.Url, out file)) + { + _ = NavigationDocumentationFileLookup.Remove(file); + NavigationDocumentationFileLookup.Add(file, item); + } + } + + private bool TryResolvePageSource(Uri page, out IDocumentationFile file) + { + file = null!; + var pagePath = GetUriPath(page); + if (string.IsNullOrEmpty(pagePath)) + return false; + + var segments = pagePath.Split('/', StringSplitOptions.RemoveEmptyEntries); + for (var length = segments.Length; length > 0; length--) + { + var tocPath = string.Join('/', segments.Take(length)); + if (!Nodes.TryGetValue(CreateTocUri(page.Scheme, tocPath), out var node)) + continue; + + var remainingPath = string.Join('/', segments.Skip(length)); + if (string.IsNullOrEmpty(remainingPath)) + { + file = node.Index.Model; + return true; + } + + if (TryFindDocumentationFile(node, remainingPath, out file)) + return true; + } + + return false; + } + + private static bool TryFindDocumentationFile( + INavigationItem item, + string remainingPath, + out IDocumentationFile file + ) + { + switch (item) + { + case ILeafNavigationItem leaf when MatchesRemainingPath(leaf.Url, remainingPath): + file = leaf.Model; + return true; + case INodeNavigationItem node: + if (MatchesRemainingPath(node.Index.Url, remainingPath)) + { + file = node.Index.Model; + return true; + } + foreach (var child in node.NavigationItems) + { + if (TryFindDocumentationFile(child, remainingPath, out file)) + return true; + } + break; + case INodeNavigationItem node: + foreach (var child in node.NavigationItems) + { + if (TryFindDocumentationFile(child, remainingPath, out file)) + return true; + } + break; + } + + file = null!; + return false; + } + + private static bool MatchesRemainingPath(string url, string remainingPath) + { + var normalizedPath = NormalizePagePath(remainingPath); + return url.TrimEnd('/').EndsWith($"/{normalizedPath}", StringComparison.OrdinalIgnoreCase); + } + + private static string NormalizePagePath(string path) + { + var normalized = path.Trim('/'); + if (normalized.EndsWith(".md", StringComparison.OrdinalIgnoreCase)) + normalized = normalized[..^3]; + if (normalized.EndsWith("/index", StringComparison.OrdinalIgnoreCase)) + normalized = normalized[..^6]; + return normalized; + } + + private static Uri CreateTocUri(string scheme, string path) + { + var slash = path.IndexOf('/'); + return slash < 0 + ? new Uri($"{scheme}://{path}") + : new Uri($"{scheme}://{path[..slash]}/{path[(slash + 1)..]}"); + } + + private static string GetUriPath(Uri uri) => (uri.Host + uri.AbsolutePath).Trim('/'); + + private static void CollectDocumentationFilesByUrl( + INavigationItem item, + Dictionary urlToFile, + string sitePrefix + ) + { + switch (item) + { + case ILeafNavigationItem leaf: + AddDocumentationFileUrl(urlToFile, leaf.Url, leaf.Model, sitePrefix); + break; + case INodeNavigationItem node: + AddDocumentationFileUrl(urlToFile, node.Url, node.Index.Model, sitePrefix); + AddDocumentationFileUrl(urlToFile, node.Index.Url, node.Index.Model, sitePrefix); + foreach (var child in node.NavigationItems) + CollectDocumentationFilesByUrl(child, urlToFile, sitePrefix); + break; + case INodeNavigationItem node: + foreach (var child in node.NavigationItems) + CollectDocumentationFilesByUrl(child, urlToFile, sitePrefix); + break; + } + } + + private static void AddDocumentationFileUrl( + Dictionary urlToFile, + string url, + IDocumentationFile file, + string sitePrefix + ) + { + _ = urlToFile.TryAdd(url, file); + + if (sitePrefix == "/" || string.IsNullOrEmpty(url) || !url.StartsWith('/')) + return; + + _ = urlToFile.TryAdd($"{sitePrefix.TrimEnd('/')}{url}", file); + } + + private static IReadOnlyList BuildV2Items( + IReadOnlyList v2Items, + IReadOnlyDictionary> nodes, + INodeNavigationItem parent, + string sitePrefix + ) => + v2Items + .Select(item => CreateV2NavigationItem(item, nodes, parent, sitePrefix)) + .Where(navItem => navItem is not null) + .Cast() + .ToList(); + + private static INavigationItem? CreateV2NavigationItem( + INavV2Item item, + IReadOnlyDictionary> nodes, + INodeNavigationItem parent, + string sitePrefix + ) => + item switch + { + SectionNavV2Item section => CreateSection(section, nodes, parent, sitePrefix), + IslandNavV2Item island => CreateIsland(island, nodes, parent), + LabelNavV2Item label => CreateLabel(label, nodes, parent, sitePrefix), + GroupNavV2Item group => CreateGroup(group, nodes, parent, sitePrefix), + TocNavV2Item toc => CreateToc(toc, nodes, parent, sitePrefix), + PageNavV2Item { Page: null, Title: var title } => new PlaceholderNavigationLeaf(title ?? "Untitled", sitePrefix, parent), + PageNavV2Item { Page: var page, Title: var title } => new PageCrossLinkLeaf(page, title ?? page.ToString(), sitePrefix, parent), + _ => null + }; + + private static INavigationItem? CreateToc( + TocNavV2Item toc, + IReadOnlyDictionary> nodes, + INodeNavigationItem parent, + string sitePrefix + ) + { + if (!nodes.TryGetValue(toc.Source, out var node)) + return null; + if (toc.Children.Count == 0) + return node; + var children = BuildV2Items(toc.Children, nodes, parent, sitePrefix); + return new TocOverrideNode(node, children, parent.NavigationRoot, parent); + } + + /// + /// Wraps an existing toc node but replaces its children with a V2-specified subset. + /// Used when a toc: entry in navigation-v2.yml declares explicit children, + /// so we can show fewer items without mutating the shared V1 node. + /// + private sealed class TocOverrideNode( + IRootNavigationItem source, + IReadOnlyList children, + IRootNavigationItem navigationRoot, + INodeNavigationItem parent + ) : INodeNavigationItem + { + public string Id => source.Id; + public string Url => source.Url; + public string NavigationTitle => source.NavigationTitle; + public IRootNavigationItem NavigationRoot => navigationRoot; + public INodeNavigationItem? Parent { get; set; } = parent; + public bool Hidden => source.Hidden; + public int NavigationIndex { get; set; } + public ILeafNavigationItem Index => source.Index; + public IReadOnlyCollection NavigationItems => children; + } + + private static SectionNavigationNode CreateSection( + SectionNavV2Item section, + IReadOnlyDictionary> nodes, + INodeNavigationItem parent, + string sitePrefix + ) + { + var placeholder = new SectionNavigationNode(section.Label, section.Url, section.Isolated, section.Dropdown, [], parent); + var children = BuildV2Items(section.Children, nodes, placeholder, sitePrefix); + return new SectionNavigationNode(section.Label, section.Url, section.Isolated, section.Dropdown, children, parent); + } + + private static INavigationItem? CreateIsland( + IslandNavV2Item island, + IReadOnlyDictionary> nodes, + INodeNavigationItem parent + ) + { + if (!nodes.TryGetValue(island.Source, out var node)) + return null; + return new IslandNavigationNode(island.Label, node, parent); + } + + private static LabelNavigationNode CreateLabel( + LabelNavV2Item label, + IReadOnlyDictionary> nodes, + INodeNavigationItem parent, + string sitePrefix + ) + { + var placeholder = new LabelNavigationNode(label.Label, label.Expanded, [], parent); + var children = BuildV2Items(label.Children, nodes, placeholder, sitePrefix); + return new LabelNavigationNode(label.Label, label.Expanded, children, parent); + } + + private static INodeNavigationItem CreateGroup( + GroupNavV2Item group, + IReadOnlyDictionary> nodes, + INodeNavigationItem parent, + string sitePrefix + ) + { + if (group.Page is not null) + { + var folderPlaceholder = new PageFolderNavigationNode(group.Title, group.Page, sitePrefix, [], parent); + var folderChildren = BuildV2Items(group.Children, nodes, folderPlaceholder, sitePrefix); + return new PageFolderNavigationNode(group.Title, group.Page, sitePrefix, folderChildren, parent); + } + var placeholder = new PlaceholderNavigationNode(group.Title, sitePrefix, [], parent); + var children = BuildV2Items(group.Children, nodes, placeholder, sitePrefix); + return new PlaceholderNavigationNode(group.Title, sitePrefix, children, parent); + } +} diff --git a/src/Elastic.Documentation.Site/Assets/main.ts b/src/Elastic.Documentation.Site/Assets/main.ts index 01e000de1c..cc0fc57cff 100644 --- a/src/Elastic.Documentation.Site/Assets/main.ts +++ b/src/Elastic.Documentation.Site/Assets/main.ts @@ -7,6 +7,7 @@ import { initImageCarousel } from './image-carousel' import { initMermaid } from './mermaid' import { openDetailsWithAnchor } from './open-details-with-anchor' import { initNav } from './pages-nav' +import { initNavV2 } from './pages-nav-v2' import { initSmoothScroll } from './smooth-scroll' import { initTabs } from './tabs' import { initializeOtel } from './telemetry/instrumentation' @@ -100,13 +101,7 @@ function initMath() { }) } -// Initialize on initial page load -document.addEventListener('DOMContentLoaded', function () { - initMath() - initMermaid() -}) - -document.addEventListener('htmx:load', function () { +function initPageAfterContentSwap() { initTocNav() initHighlight() initCopyButton() @@ -114,7 +109,12 @@ document.addEventListener('htmx:load', function () { initAppliesSwitch() initMath() initMermaid() - initNav() + const v2Nav = document.querySelector('[data-nav-v2]') + if (v2Nav) { + initNavV2(v2Nav) + } else { + initNav() + } initSmoothScroll() openDetailsWithAnchor() @@ -126,6 +126,20 @@ document.addEventListener('htmx:load', function () { if (editParam) { $('.edit-this-page.hidden')?.classList.remove('hidden') } +} + +// htmx defers the initial htmx:load to setTimeout(0); prime V2 sidebar on first paint so isolated serve shows behaviour before that tick. +document.addEventListener('DOMContentLoaded', function () { + initMath() + initMermaid() + const v2Nav = document.querySelector('[data-nav-v2]') + if (v2Nav) { + initNavV2(v2Nav) + } +}) + +document.addEventListener('htmx:load', function () { + initPageAfterContentSwap() }) // Don't remove style tags because they are used by the elastic global nav. diff --git a/src/Elastic.Documentation.Site/Assets/markdown/hub.css b/src/Elastic.Documentation.Site/Assets/markdown/hub.css new file mode 100644 index 0000000000..1a9f814d0b --- /dev/null +++ b/src/Elastic.Documentation.Site/Assets/markdown/hub.css @@ -0,0 +1,663 @@ +/* + * Hub-page styling. Models the gist prototypes in docs/hubs/. Class names + * mirror the directive output: + * + * .hub-page page wrapper (set by Layout/_Layout.cshtml's RenderHub) + * .hub-content article wrapper inside the content column + * .hub-hero hero section (full-bleed dark background) + * .hub-on-this-page inline TOC chip + * .hub-intro getting-started callout panel + * .hub-whats-new What's new panel + * .hub-zone section heading + * .hub-card-grid card grid container + * .hub-card card item + * + * The hero / on-this-page / intro / whats-new / card-grid containers all + * use a shared inner rail of max-w-5xl + 24px horizontal padding. + */ + +@layer components { + /* Page wrapper: full-width content. Stays white so the page reads + consistently whether the user lands via full navigation or via an + htmx-boosted swap from a regular docs page. */ + .hub-page .hub-content { + @apply mb-20 w-full max-w-none p-0; + } + + /* Hero ------------------------------------------------------------- */ + .hub-hero { + background-color: var(--color-ink-navy); + color: var(--color-white); + padding: 56px 24px 48px; + } + .hub-hero .hub-hero-inner { + @apply mx-auto w-full max-w-5xl; + } + .hub-hero .hub-hero-top { + display: grid; + grid-template-columns: auto 1fr; + column-gap: 16px; + align-items: center; + margin-bottom: 12px; + } + .hub-hero .hub-hero-title { + display: contents; + } + .hub-hero .hub-hero-title h1 { + grid-column: 2; + grid-row: 1; + } + .hub-hero .hub-hero-title p { + grid-column: 1 / -1; + grid-row: 2; + margin-top: 16px; + } + .hub-hero .hub-hero-icon { + @apply inline-flex h-13 w-13 shrink-0 items-center justify-center rounded-xl text-2xl font-bold; + width: 52px; + height: 52px; + background-color: rgb(255 255 255 / 0.08); + color: var(--color-white); + } + .hub-hero .hub-hero-icon-svg { + background-color: transparent; + } + .hub-hero .hub-hero-icon-svg svg { + width: 32px; + height: 32px; + } + .hub-hero .hub-hero-title h1 { + font-size: 2.571rem; + font-weight: 700; + line-height: 1.2; + letter-spacing: -0.5px; + color: var(--color-white); + margin: 0; + } + .hub-hero .hub-hero-title h1 a.headerlink { + color: var(--color-white); + text-decoration: none; + } + .hub-hero .hub-hero-title p { + font-size: 1.143rem; + color: rgb(255 255 255 / 0.7); + max-width: 720px; + line-height: 1.6; + } + + .hub-hero .hub-hero-search { + margin-top: 20px; + margin-bottom: 20px; + max-width: 480px; + } + .hub-hero .hub-hero-search-box { + display: flex; + align-items: center; + gap: 10px; + background: var(--color-white); + border-radius: 10px; + padding: 10px 16px; + box-shadow: 0 2px 12px rgb(0 0 0 / 0.15); + color: var(--color-grey-80); + cursor: text; + } + .hub-hero .hub-hero-search-icon { + flex-shrink: 0; + } + .hub-hero .hub-hero-search-box input { + flex: 1; + border: none; + outline: none; + background: transparent; + font-size: 15px; + font-family: inherit; + color: var(--color-ink-dark); + min-width: 0; + } + .hub-hero .hub-hero-search-box input::placeholder { + color: var(--color-grey-40); + } + + .hub-hero .hub-hero-meta { + @apply mt-2 flex flex-wrap items-center gap-3; + } + + .hub-hero .hub-hero-version { + display: inline-flex; + align-items: center; + gap: 8px; + background: rgb(255 255 255 / 0.08); + border: 1px solid rgb(255 255 255 / 0.15); + border-radius: 20px; + padding: 5px 14px; + font-size: 13px; + color: rgb(255 255 255 / 0.85); + } + .hub-hero .hub-hero-version-dot { + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + background: #9adc30; + } + + /* Version dropdown ------------------------------------------------- */ + .hub-hero details.hub-hero-version-dropdown { + position: relative; + display: inline-block; + padding: 0; + border: 0; + background: transparent; + } + .hub-hero details.hub-hero-version-dropdown summary { + display: inline-flex; + align-items: center; + gap: 10px; + background: rgb(255 255 255 / 0.12); + border: 1px solid rgb(255 255 255 / 0.25); + border-radius: 999px; + padding: 4px 4px 4px 14px; + font-size: 13px; + color: var(--color-white); + cursor: pointer; + list-style: none; + user-select: none; + transition: + background 0.15s, + border-color 0.15s; + } + .hub-hero + details.hub-hero-version-dropdown + summary::-webkit-details-marker { + display: none; + } + .hub-hero details.hub-hero-version-dropdown summary:hover { + background: rgb(255 255 255 / 0.2); + border-color: rgb(255 255 255 / 0.4); + } + .hub-hero details[open].hub-hero-version-dropdown summary { + background: rgb(255 255 255 / 0.2); + border-color: rgb(255 255 255 / 0.4); + } + .hub-hero .hub-hero-version-caret { + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border-radius: 50%; + background: rgb(255 255 255 / 0.22); + color: var(--color-white); + transition: + transform 0.18s ease, + background 0.15s; + } + .hub-hero .hub-hero-version-caret svg { + display: block; + width: 16px; + height: 16px; + } + .hub-hero + details.hub-hero-version-dropdown + summary:hover + .hub-hero-version-caret { + background: rgb(255 255 255 / 0.3); + } + .hub-hero details[open] .hub-hero-version-caret { + transform: rotate(180deg); + } + + .hub-hero .hub-hero-version-menu { + position: absolute; + top: calc(100% + 6px); + left: 0; + min-width: 220px; + background: var(--color-white); + border: 1px solid var(--color-grey-20); + border-radius: 10px; + padding: 4px; + margin: 0; + list-style: none; + box-shadow: 0 8px 24px rgb(0 0 0 / 0.15); + font-size: 13px; + color: var(--color-ink-dark); + z-index: 20; + } + .hub-hero .hub-hero-version-menu li { + margin: 0; + padding: 0; + border-radius: 6px; + } + .hub-hero .hub-hero-version-menu li a, + .hub-hero + .hub-hero-version-menu + li + > span:not(.hub-hero-version-tick):not(.hub-hero-version-soon) { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 8px 12px; + text-decoration: none; + color: inherit; + white-space: nowrap; + } + .hub-hero + .hub-hero-version-menu + li + > span:not(.hub-hero-version-tick):not(.hub-hero-version-soon) { + display: flex; + } + .hub-hero .hub-hero-version-menu li { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 8px 12px; + white-space: nowrap; + } + .hub-hero .hub-hero-version-menu li a, + .hub-hero .hub-hero-version-menu li > span { + padding: 0; + } + .hub-hero + .hub-hero-version-menu + li:hover:not(.hub-hero-version-current):not( + .hub-hero-version-disabled + ) { + background: var(--color-grey-10); + } + .hub-hero .hub-hero-version-menu li a { + color: inherit; + text-decoration: none; + flex: 1; + } + .hub-hero .hub-hero-version-menu li a:hover { + color: var(--color-blue-elastic-100); + text-decoration: none; + } + .hub-hero .hub-hero-version-current { + font-weight: 600; + color: var(--color-ink-dark); + } + .hub-hero .hub-hero-version-tick { + color: var(--color-blue-elastic-100); + font-weight: 700; + } + .hub-hero .hub-hero-version-disabled { + color: var(--color-grey-40); + cursor: not-allowed; + } + .hub-hero .hub-hero-version-soon { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--color-grey-40); + background: var(--color-grey-10); + padding: 2px 6px; + border-radius: 4px; + } + + .hub-hero .hub-hero-pills { + @apply m-0 flex list-none flex-wrap gap-2 p-0; + } + .hub-hero .hub-hero-pills li { + @apply m-0 p-0; + } + .hub-hero .hub-hero-pills a { + font-size: 13px; + padding: 5px 14px; + border-radius: 20px; + background: rgb(255 255 255 / 0.06); + border: 1px solid rgb(255 255 255 / 0.12); + color: rgb(255 255 255 / 0.85); + text-decoration: none; + transition: background 0.15s; + } + .hub-hero .hub-hero-pills a:hover { + background: rgb(255 255 255 / 0.12); + color: var(--color-white); + text-decoration: none; + } + + .hub-hero .hub-hero-releases { + @apply mt-4; + font-size: 12px; + color: rgb(255 255 255 / 0.45); + } + .hub-hero .hub-hero-releases a { + color: rgb(255 255 255 / 0.6); + text-decoration: none; + } + .hub-hero .hub-hero-releases a:hover { + color: rgb(255 255 255 / 0.85); + text-decoration: underline; + } + + /* On this page ----------------------------------------------------- */ + .hub-on-this-page { + @apply mx-auto w-full max-w-5xl; + margin-top: 40px; + margin-bottom: 36px; + padding: 20px 28px; + background: var(--color-white); + border: 1px solid var(--color-grey-20); + border-radius: 16px; + font-size: 14px; + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 4px 0; + line-height: 1.9; + } + .hub-on-this-page .hub-on-this-page-label { + font-weight: 700; + color: var(--color-ink-dark); + margin-right: 14px; + } + .hub-on-this-page a { + font-weight: 500; + color: var(--color-blue-elastic-100); + text-decoration: none; + } + .hub-on-this-page a:hover { + text-decoration: underline; + } + .hub-on-this-page .hub-on-this-page-sep { + color: var(--color-grey-30); + margin: 0 10px; + font-size: 12px; + } + + /* Intro callout ---------------------------------------------------- */ + .hub-intro { + @apply mx-auto w-full max-w-5xl; + margin-bottom: 36px; + background: var(--color-white); + border: 1px solid var(--color-grey-20); + border-left: 3px solid var(--color-teal-40, #66ccc8); + border-radius: 16px; + padding: 18px 24px; + font-size: 14px; + color: var(--color-ink); + line-height: 1.5; + } + .hub-intro p { + margin: 0; + } + .hub-intro a { + color: var(--color-blue-elastic-100); + text-decoration: underline; + } + + /* What's new ------------------------------------------------------- */ + .hub-whats-new { + @apply mx-auto w-full max-w-5xl; + background: var(--color-white); + border: 1px solid var(--color-grey-20); + border-radius: 16px; + padding: 24px 28px; + margin-bottom: 36px; + scroll-margin-top: 120px; + } + .hub-whats-new .hub-wn-header { + @apply mb-4 flex items-center gap-2; + } + .hub-whats-new .hub-wn-badge { + font-size: 12px; + font-weight: 700; + color: var(--color-white); + background: #f04e98; + border-radius: 6px; + padding: 3px 10px; + line-height: 1.3; + } + .hub-whats-new .hub-wn-title { + font-size: 15px; + font-weight: 700; + color: var(--color-ink-dark); + } + .hub-whats-new .hub-wn-rn-links { + margin-left: auto; + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + flex-shrink: 0; + color: var(--color-ink-light); + } + .hub-whats-new .hub-wn-rn-label { + color: var(--color-ink-light); + } + .hub-whats-new .hub-wn-rn-links a { + font-weight: 500; + color: var(--color-blue-elastic-100); + text-decoration: none; + } + .hub-whats-new .hub-wn-rn-links a:hover { + text-decoration: underline; + } + .hub-whats-new .hub-wn-rn-sep { + color: var(--color-grey-30); + } + + .hub-whats-new .hub-wn-items { + @apply m-0 flex list-none flex-col gap-2 p-0; + } + .hub-whats-new .hub-wn-item { + @apply m-0 p-0; + } + .hub-whats-new .hub-wn-item-link { + display: grid; + grid-template-columns: auto minmax(0, 1fr) 130px; + align-items: baseline; + column-gap: 12px; + padding: 10px 14px; + border-radius: 10px; + background: var(--color-grey-10); + text-decoration: none; + font-size: 14px; + color: var(--color-body, #515151); + line-height: 1.4; + border: 1px solid transparent; + transition: background 0.12s; + } + .hub-whats-new a.hub-wn-item-link:hover { + background: var(--color-blue-elastic-10); + border-color: var(--color-grey-20); + text-decoration: none; + } + .hub-whats-new .hub-wn-item-title { + color: var(--color-ink-dark); + white-space: nowrap; + } + .hub-whats-new .hub-wn-item-desc { + color: var(--color-ink-light); + } + .hub-whats-new .hub-wn-item-meta { + justify-self: end; + text-align: right; + white-space: nowrap; + font-size: 12px; + color: var(--color-grey-80); + font-weight: 500; + } + .hub-whats-new .hub-wn-item-desc { + min-width: 0; + } + + /* Zone (section heading) ------------------------------------------ */ + .hub-zone { + @apply mx-auto w-full max-w-5xl; + margin-top: 56px; + margin-bottom: 24px; + scroll-margin-top: 120px; + } + .hub-zone:first-of-type { + margin-top: 0; + } + .hub-zone .hub-zone-title { + font-size: 1.571rem; + font-weight: 700; + color: var(--color-ink-dark); + line-height: 1.2; + margin: 0; + } + .hub-zone .hub-zone-intro { + color: var(--color-ink-light); + margin-top: 8px; + } + + /* Card grid -------------------------------------------------------- */ + .hub-card-grid { + @apply m-0 mx-auto w-full max-w-5xl list-none p-0; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); + gap: 24px; + } + + /* Card ------------------------------------------------------------- */ + .hub-card { + background: var(--color-white); + border: 1px solid var(--color-grey-20); + border-radius: 16px; + padding: 28px 28px 24px; + transition: + box-shadow 0.15s, + border-color 0.15s; + margin: 0; + list-style: none; + } + .hub-card:hover { + border-color: var(--color-grey-30); + box-shadow: 0 2px 8px rgb(0 0 0 / 0.05); + } + + .hub-card .hub-card-head { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 6px; + } + .hub-card .hub-card-icon svg { + width: 28px; + height: 28px; + display: block; + } + .hub-card .hub-card-title { + font-size: 1.214rem; + font-weight: 700; + color: var(--color-ink-dark); + margin: 0; + } + .hub-card .hub-card-title a { + color: var(--color-ink-dark); + text-decoration: none; + } + .hub-card .hub-card-title a:hover { + color: var(--color-blue-elastic); + text-decoration: none; + } + + .hub-card .hub-card-desc { + font-size: 14px; + color: var(--color-ink-light); + margin-bottom: 14px; + line-height: 1.5; + } + + .hub-card .hub-card-links { + list-style: none; + display: flex; + flex-direction: column; + gap: 2px; + margin: 0; + padding: 0; + } + .hub-card .hub-card-links li { + margin: 0; + padding: 0; + } + .hub-card .hub-card-links li a { + font-size: 14px; + font-weight: 600; + color: var(--color-blue-elastic-100); + padding: 3px 0; + display: inline-flex; + align-items: baseline; + gap: 6px; + text-decoration: none; + } + .hub-card .hub-card-links li a:hover { + color: var(--color-blue-elastic-110); + text-decoration: underline; + } + .hub-card .hub-card-links li a::before { + content: '\203A'; + color: var(--color-grey-40); + font-weight: 700; + font-size: 15px; + } + + /* Card aside (Panel types, Chart types, Key features...) */ + .hub-card .hub-card-aside { + margin-top: 14px; + padding-top: 12px; + border-top: 1px solid var(--color-grey-20); + } + .hub-card .hub-card-aside-label { + font-size: 12px; + color: var(--color-grey-80); + margin-bottom: 6px; + font-weight: 600; + letter-spacing: 0.3px; + } + .hub-card .hub-card-aside-links { + display: flex; + flex-wrap: wrap; + gap: 0; + font-size: 13px; + line-height: 1.9; + } + .hub-card .hub-card-aside-links a { + color: var(--color-blue-elastic-100); + font-weight: 500; + text-decoration: none; + } + .hub-card .hub-card-aside-links a:hover { + text-decoration: underline; + } + .hub-card .hub-card-aside-sep { + color: var(--color-grey-30); + margin: 0 6px; + } + + /* Solution accent borders */ + .hub-card-sol { + border-left: 3px solid var(--color-blue-elastic); + } + .hub-card-sol-es { + border-left-color: #fec514; + } + .hub-card-sol-obs { + border-left-color: #f04e98; + } + .hub-card-sol-sec { + border-left-color: #00bfb3; + } + + @media (max-width: 768px) { + .hub-hero h1 { + font-size: 2rem; + } + .hub-card-grid { + grid-template-columns: 1fr; + } + .hub-on-this-page { + flex-direction: column; + } + .hub-card { + padding: 20px; + } + } +} diff --git a/src/Elastic.Documentation.Site/Assets/markdown/metadata-box.css b/src/Elastic.Documentation.Site/Assets/markdown/metadata-box.css new file mode 100644 index 0000000000..7d90dca7fc --- /dev/null +++ b/src/Elastic.Documentation.Site/Assets/markdown/metadata-box.css @@ -0,0 +1,283 @@ +/* + * Page-top "Availability" box. Sits below the H1 and consolidates two + * page-level metadata systems in a single container: + * + * - Product hub pills, in the summary row alongside the "Availability" + * label, for navigation to each product's hub page. + * - applies_to-driven rows (Versions / Deployments) below the summary, + * collapsible via native
    , expanded by default. + * + * Hub layout (and any frontmatter Layout) opts out -- the partial returns + * early. The box only renders when at least one of pills or rows exists. + */ + +@layer components { + .metadata-box { + margin: 16px 0 24px 0; + padding: 8px 14px; + background: var(--color-grey-10); + border: 1px solid var(--color-grey-20); + border-radius: 6px; + font-size: 13px; + line-height: 1.4; + } + + .metadata-box-heading { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px 12px; + cursor: pointer; + list-style: none; + padding: 2px 0 6px 0; + user-select: none; + } + .metadata-box-heading::-webkit-details-marker { + display: none; + } + .metadata-box-heading:focus-visible { + outline: 2px solid var(--color-blue-elastic); + outline-offset: 2px; + border-radius: 3px; + } + + .metadata-box-summary-only .metadata-box-heading { + cursor: default; + } + + /* Title sits one step above the row labels in the visual hierarchy: + slightly larger, heavier weight, full ink color. Row labels stay + smaller and lighter, so the title reads as the box's heading and + the pills next to it become the headlined entries. */ + .metadata-box-title { + flex: 0 0 auto; + font-size: 15px; + font-weight: 700; + color: var(--color-ink-dark); + } + + .metadata-box-pills { + display: inline-flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; + flex: 1 1 auto; + } + + .metadata-box-chevron { + flex-shrink: 0; + margin-left: auto; + color: var(--color-grey-60); + transition: transform 0.15s ease; + } + .metadata-box[open] .metadata-box-chevron { + transform: rotate(180deg); + } + + .metadata-box-rows { + display: flex; + flex-direction: column; + gap: 4px; + margin-top: 6px; + padding-top: 10px; + border-top: 1px solid var(--color-grey-20); + } + + .metadata-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px 10px; + min-height: 24px; + } + + .metadata-label { + flex: 0 0 auto; + min-width: 100px; + font-weight: 600; + color: var(--color-grey-80); + } + + .metadata-values { + display: inline-flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; + min-width: 0; + } + + /* + * Render applies_to entries as plain comma-separated text + a small + * "?" icon that carries the existing popover tooltip. Implemented in + * CSS so we don't touch the popover web component: + * - Override the global `display: contents` on the popover host so + * it has its own box (and ::before content can render). + * - Use ::before with `attr(badge-key)` to print the entry's name. + * - Reskin the visible `.applicable-info` pill into a 16px "?" + * circle that still triggers the tooltip on hover/click. + */ + .metadata-values.applies applies-to-popover { + display: inline-flex; + align-items: center; + color: var(--color-ink-dark); + } + .metadata-values applies-to-popover::before { + content: attr(badge-key); + } + .metadata-values applies-to-popover:not(:first-child)::before { + content: ', ' attr(badge-key); + } + + .metadata-values .applicable-info { + all: unset; + display: inline-flex; + align-items: center; + justify-content: center; + width: 14px; + height: 14px; + margin-left: 4px; + border-radius: 999px; + background: var(--color-grey-20); + color: var(--color-grey-80); + font-size: 10px; + font-weight: 700; + line-height: 1; + cursor: help; + transition: + background-color 0.15s ease, + color 0.15s ease; + } + .metadata-values .applicable-info::before { + content: '?'; + } + .metadata-values .applicable-info > * { + display: none; + } + .metadata-values .applicable-info:hover { + background: var(--color-grey-30); + color: var(--color-ink-dark); + } + + /* Unavailable items (sorted to the end by ApplicableToViewModel) render + dimmed + struck-through to read as "not in scope" rather than as a + peer alternative. */ + .metadata-values applies-to-popover[lifecycle-class='unavailable']::before { + text-decoration: line-through; + text-decoration-color: var(--color-ink-light); + color: var(--color-ink-light); + } + .metadata-values + applies-to-popover[lifecycle-class='unavailable'] + .applicable-info { + opacity: 0.65; + } + + /* + * Product hub pills (used inside the metadata box's summary row). + * Brand-color dot + display name + arrow. Rendered as anchors, so we + * override `.markdown-content a` (blue + underline) explicitly -- + * `!important` is the safety net against utility-layer rules that beat + * us on cascade order. + */ + .markdown-content a.product-badge, + a.product-badge { + display: inline-flex; + align-items: center; + gap: 7px; + padding: 2px 10px; + border-radius: 999px; + background: var(--color-white); + border: 1px solid var(--color-grey-20); + color: var(--color-ink-dark) !important; + font-size: 12px; + font-weight: 600; + text-decoration: none !important; + line-height: 1.3; + transition: + border-color 0.15s ease, + background-color 0.15s ease, + color 0.15s ease, + box-shadow 0.15s ease, + transform 0.15s ease; + } + .markdown-content a.product-badge:hover, + a.product-badge:hover { + text-decoration: none !important; + color: var(--color-ink-dark) !important; + box-shadow: 0 1px 3px rgb(0 0 0 / 0.08); + transform: translateY(-1px); + } + + .product-badge-arrow { + flex-shrink: 0; + color: var(--color-grey-50); + font-size: 13px; + line-height: 1; + margin-left: 1px; + transition: + transform 0.15s ease, + color 0.15s ease; + } + .product-badge:hover .product-badge-arrow { + color: var(--color-ink-light); + transform: translateX(2px); + } + .product-badge:focus-visible { + outline: 2px solid var(--color-blue-elastic); + outline-offset: 2px; + } + + .product-badge-dot { + display: inline-block; + width: 7px; + height: 7px; + border-radius: 999px; + background: var(--color-grey-40); + flex-shrink: 0; + } + .product-badge-label { + display: inline-block; + white-space: nowrap; + } + + /* Per-product accents -- mirrors hub solution-card border colors. */ + .product-badge-elasticsearch .product-badge-dot { + background: #fec514; + } + .product-badge-elasticsearch:hover { + border-color: #fec514; + background: rgb(254 197 20 / 0.06); + } + .product-badge-kibana .product-badge-dot { + background: #f04e98; + } + .product-badge-kibana:hover { + border-color: #f04e98; + background: rgb(240 78 152 / 0.05); + } + .product-badge-observability .product-badge-dot { + background: #0077cc; + } + .product-badge-observability:hover { + border-color: #0077cc; + background: rgb(0 119 204 / 0.05); + } + .product-badge-security .product-badge-dot { + background: #00bfb3; + } + .product-badge-security:hover { + border-color: #00bfb3; + background: rgb(0 191 179 / 0.06); + } + + @media (max-width: 640px) { + .metadata-row { + flex-direction: column; + align-items: flex-start; + gap: 4px; + } + .metadata-label { + min-width: 0; + } + } +} diff --git a/src/Elastic.Documentation.Site/Assets/pages-nav-v2.ts b/src/Elastic.Documentation.Site/Assets/pages-nav-v2.ts new file mode 100644 index 0000000000..7582c5e01b --- /dev/null +++ b/src/Elastic.Documentation.Site/Assets/pages-nav-v2.ts @@ -0,0 +1,716 @@ +import { $$ } from 'select-dom' +import tippy from 'tippy.js' +import type { Instance } from 'tippy.js' + +const navV2CollapsedStorageKey = 'docs-builder-nav-v2-collapsed-ids' + +let navV2FolderLinkToggleBound = false +let navV2OptimisticNavigateBound = false + +let navV2TruncationTippyInstances: Instance[] = [] + +function readCollapsedFolderIds(): Set { + try { + const raw = sessionStorage.getItem(navV2CollapsedStorageKey) + if (!raw) { + return new Set() + } + + const parsed = JSON.parse(raw) as unknown + if (!Array.isArray(parsed)) { + return new Set() + } + + return new Set(parsed.filter((x): x is string => typeof x === 'string')) + } catch { + return new Set() + } +} + +function writeCollapsedFolderIds(ids: Set) { + sessionStorage.setItem(navV2CollapsedStorageKey, JSON.stringify([...ids])) +} + +function persistFolderCheckboxCollapsedState(cb: HTMLInputElement) { + if (!cb.id) { + return + } + + const ids = readCollapsedFolderIds() + if (!cb.checked) { + ids.add(cb.id) + } else { + ids.delete(cb.id) + } + + writeCollapsedFolderIds(ids) +} + +function normalizeDocPathname(pathname: string) { + const p = pathname.replace(/\/$/, '') + return p === '' ? '/' : p +} + +/** + * Returns true when the current page is the section root URL. + * Section root pages should not get current-page highlighting in the sidebar + * because the section URL is a tab target, not a page within the nav tree. + */ +function isOnSectionRootPage(nav: HTMLElement): boolean { + const sectionUrl = nav.dataset.sectionUrl + if (!sectionUrl) { + return false + } + + return ( + normalizeDocPathname(window.location.pathname) === + normalizeDocPathname(sectionUrl) + ) +} + +/** Matches {@link markCurrentPage} / {@link expandToCurrentPage} href selectors (not root-normalized). */ +function stripTrailingSlashForNavHref(pathname: string) { + return pathname.replace(/\/$/, '') +} + +function linkPathMatchesCurrentPage(anchor: HTMLAnchorElement) { + const href = anchor.getAttribute('href') + if (!href) { + return false + } + + const linkPath = normalizeDocPathname( + new URL(href, window.location.href).pathname + ) + const currentPath = normalizeDocPathname(window.location.pathname) + return linkPath === currentPath +} + +/** + * Primary click on a folder row link: + * - When the current page matches the folder row href, toggle expand/collapse only (preventDefault). + * - When the folder is already expanded but the URL does not match (typical for placeholder + * groups like `/_placeholder/...`), toggle closed so a second click collapses the section. + * - Otherwise open the folder and allow navigation to the row href (e.g. section index). + * Skips modified clicks (new tab, etc.). Collapsed folder ids are stored for expandToCurrentPage. + */ +function ensureNavV2FolderLinkToggle() { + if (navV2FolderLinkToggleBound) { + return + } + + navV2FolderLinkToggleBound = true + + document.addEventListener( + 'click', + (e: MouseEvent) => { + if (!(e.target instanceof Element)) { + return + } + + if ( + e.defaultPrevented || + e.button !== 0 || + e.metaKey || + e.ctrlKey || + e.shiftKey || + e.altKey + ) { + return + } + + const a = e.target.closest( + '[data-nav-v2] li.group-navigation > .nav-folder-peer > a.sidebar-link' + ) as HTMLAnchorElement | null + + if (!a) { + return + } + + const peer = a.parentElement + const cb = peer?.querySelector( + ':scope > input[type="checkbox"]' + ) + + if (!cb) { + return + } + + if (linkPathMatchesCurrentPage(a)) { + cb.checked = !cb.checked + cb.dispatchEvent(new Event('change', { bubbles: true })) + persistFolderCheckboxCollapsedState(cb) + e.preventDefault() + e.stopPropagation() + return + } + + if (cb.checked) { + cb.checked = false + cb.dispatchEvent(new Event('change', { bubbles: true })) + persistFolderCheckboxCollapsedState(cb) + e.preventDefault() + e.stopPropagation() + return + } + + cb.checked = true + cb.dispatchEvent(new Event('change', { bubbles: true })) + persistFolderCheckboxCollapsedState(cb) + }, + true + ) +} + +/** + * Apply current + subtree highlight from the clicked href before HTMX finishes (hx-boost), + * so the sidebar does not wait for the network response to update. + */ +function ensureNavV2OptimisticCurrentOnNavigate() { + if (navV2OptimisticNavigateBound) { + return + } + + navV2OptimisticNavigateBound = true + + document.addEventListener( + 'click', + (e: MouseEvent) => { + if (!(e.target instanceof Element)) { + return + } + + if ( + e.defaultPrevented || + e.button !== 0 || + e.metaKey || + e.ctrlKey || + e.shiftKey || + e.altKey + ) { + return + } + + const a = e.target.closest( + 'nav[data-nav-v2] a.sidebar-link' + ) as HTMLAnchorElement | null + + if (!a || a.hasAttribute('hx-disable')) { + return + } + + const nav = a.closest('[data-nav-v2]') as HTMLElement | null + if (!nav) { + return + } + + const li = a.closest('li.group-navigation') + const folderRowInLi = + li?.querySelector( + ':scope > .nav-folder-peer > a.sidebar-link' + ) ?? null + if (folderRowInLi === a && linkPathMatchesCurrentPage(a)) { + return + } + + const href = a.getAttribute('href') + if (!href) { + return + } + + let path: string + try { + path = new URL(href, window.location.href).pathname + } catch { + return + } + + const here = window.location.pathname.replace(/\/$/, '') + const pathStripped = path.replace(/\/$/, '') + if (pathStripped === here) { + return + } + + markCurrentPageForPath(nav, path) + expandToCurrentPageForPath(nav, path) + applyActiveSubtreeHighlight(nav) + }, + true + ) +} + +/** + * Returns all sibling top-level accordion checkboxes for a given checkbox. + * Siblings are other checkboxes inside [data-v2-accordion] elements at the + * same nesting level as the given checkbox's ancestor accordion. + */ +function getSiblingAccordionCheckboxes( + checkbox: HTMLInputElement +): HTMLInputElement[] { + const accordion = checkbox.closest('[data-v2-accordion]') + if (!accordion) { + return [] + } + + const parent = accordion.parentElement + if (!parent) { + return [] + } + + return Array.from( + parent.querySelectorAll( + '[data-v2-accordion] > .peer input[type=checkbox]' + ) + ).filter((c) => c !== checkbox) +} + +/** + * Accordion behaviour: when a top-level section is opened, + * collapse all its siblings so only one section is expanded at a time. + */ +function initAccordion(nav: HTMLElement) { + nav.querySelectorAll( + '[data-v2-accordion] > .peer input[type=checkbox]' + ).forEach((cb) => { + if (cb.dataset.navV2AccordionBound === 'true') { + return + } + + cb.dataset.navV2AccordionBound = 'true' + cb.addEventListener('change', (e) => { + const target = e.target as HTMLInputElement + if (target.checked) { + getSiblingAccordionCheckboxes(target).forEach((sibling) => { + sibling.checked = false + }) + } + }) + }) +} + +function warmFolderSubtreeLayoutFromPeer(peer: HTMLElement) { + const li = peer.parentElement + if (!li?.matches('li.group-navigation')) { + return + } + + const input = li.querySelector( + ':scope > .nav-folder-peer input[type=checkbox]' + ) + if (input?.checked) { + return + } + + const ul = li.querySelector( + ':scope > .docs-sidebar-nav-v2__folder-clip .docs-sidebar-nav-v2__folder-children' + ) + if (ul) { + void ul.scrollHeight + } +} + +function primeNavV2FolderLayoutsSync(nav: HTMLElement, maxCount: number) { + const uls = nav.querySelectorAll( + 'ul.docs-sidebar-nav-v2__folder-children' + ) + const n = Math.min(maxCount, uls.length) + for (let i = 0; i < n; i++) { + const ul = uls[i] + const li = ul.closest('li.group-navigation') + const input = li?.querySelector( + ':scope > .nav-folder-peer input[type=checkbox]' + ) + if (!input?.checked) { + void ul.scrollHeight + } + } +} + +/** + * Spreads first-open layout cost off the interaction path: idle batches measure collapsed + * folder lists; pointer events on the folder row warm right before click (see init). + */ +function scheduleNavV2CollapsedFolderLayoutWarmup( + nav: HTMLElement, + startIndex: number +) { + const uls = nav.querySelectorAll( + 'ul.docs-sidebar-nav-v2__folder-children' + ) + let index = startIndex + const chunkSize = 6 + + const schedule = (cb: () => void) => { + if (typeof requestIdleCallback !== 'undefined') { + requestIdleCallback( + () => { + cb() + }, + { timeout: 2000 } + ) + } else { + setTimeout(cb, 0) + } + } + + const step = () => { + if (!nav.isConnected) { + return + } + + const end = Math.min(index + chunkSize, uls.length) + for (; index < end; index++) { + const ul = uls[index] + const li = ul.closest('li.group-navigation') + const input = li?.querySelector( + ':scope > .nav-folder-peer input[type=checkbox]' + ) + if (!input?.checked) { + void ul.scrollHeight + } + } + + if (index < uls.length) { + schedule(step) + } + } + + schedule(step) +} + +function initNavV2FolderLayoutWarmup(nav: HTMLElement) { + primeNavV2FolderLayoutsSync(nav, 14) + + nav.querySelectorAll( + 'li.group-navigation > .nav-folder-peer' + ).forEach((peer) => { + if (peer.dataset.navV2PointerWarmBound === 'true') { + return + } + + peer.dataset.navV2PointerWarmBound = 'true' + const warm = () => { + warmFolderSubtreeLayoutFromPeer(peer) + } + + peer.addEventListener('pointerenter', warm, { passive: true }) + /* + * Runs immediately before click (after hover path): pays layout once so the grid + * transition is less likely to share a frame with the first full subtree measure. + */ + peer.addEventListener('pointerdown', warm, { passive: true }) + }) + + scheduleNavV2CollapsedFolderLayoutWarmup(nav, 14) +} + +function clearActiveSubtreeHighlight(nav: HTMLElement) { + nav.querySelectorAll( + '.nav-v2-active-subtree, .nav-v2-active-leaf, .nav-v2-active-ancestor' + ).forEach((el) => { + el.classList.remove( + 'nav-v2-active-subtree', + 'nav-v2-active-leaf', + 'nav-v2-active-ancestor' + ) + }) +} + +/** + * Counts {@code li} ancestors from {@code anchor} up to (but not including) {@code nav}. + */ +function navListItemDepthFromAnchor(anchor: Element, nav: HTMLElement): number { + let depth = 0 + let el: Element | null = anchor + while (el && el !== nav) { + if (el.matches('li')) { + depth++ + } + el = el.parentElement + } + return depth +} + +/** + * Prefer the deepest {@code a.sidebar-link.current} when several share the URL (folder index + * and child, or duplicate toc entries). Otherwise {@code querySelector} picks the first in DOM + * order (usually a parent folder) and subtree/ancestor classes apply to the wrong rows. + */ +function deepestCurrentSidebarLink(nav: HTMLElement): HTMLAnchorElement | null { + const anchors = nav.querySelectorAll( + 'a.sidebar-link.current' + ) + if (anchors.length === 0) { + return null + } + + let best = anchors[0] + let bestDepth = navListItemDepthFromAnchor(best, nav) + for (let i = 1; i < anchors.length; i++) { + const candidate = anchors[i] + const d = navListItemDepthFromAnchor(candidate, nav) + if (d > bestDepth) { + bestDepth = d + best = candidate + } + } + return best +} + +/** + * Apply #F1F6FF background per design: folder index → whole folder + visible children; + * nested folder index → that folder + its children only; leaf → that row only. + */ +function applyActiveSubtreeHighlight(nav: HTMLElement) { + clearActiveSubtreeHighlight(nav) + if (isOnSectionRootPage(nav)) { + return + } + const current = deepestCurrentSidebarLink(nav) + if (!current || !nav.contains(current)) { + return + } + + const hostLi = current.closest('li') + if (!hostLi || !nav.contains(hostLi)) { + return + } + + const folderRowLink = hostLi.querySelector( + ':scope > .nav-folder-peer > a.sidebar-link' + ) + const childUl = hostLi.querySelector( + ':scope > .docs-sidebar-nav-v2__folder-clip .docs-sidebar-nav-v2__folder-children' + ) + + if ( + hostLi.classList.contains('group-navigation') && + folderRowLink && + folderRowLink === current && + childUl + ) { + hostLi.classList.add('nav-v2-active-subtree') + } else { + hostLi.classList.add('nav-v2-active-leaf') + } + + /* + * Walk DOM ancestors: folder rows (li.group-navigation) whose own link is not .current + * get nav-v2-active-ancestor (CSS: semibold + #1D2A3E on clickable rows only). + */ + let walk: Element | null = hostLi + while (walk && walk !== nav) { + if (walk.matches('li.group-navigation')) { + const ancestorRow = walk.querySelector( + ':scope > .nav-folder-peer > a.sidebar-link' + ) + if (ancestorRow && ancestorRow !== current) { + walk.classList.add('nav-v2-active-ancestor') + } + } + + walk = walk.parentElement + } +} + +/** + * Mark all nav links whose href matches {@code pathname} with the "current" CSS class. + */ +function markCurrentPageForPath(nav: HTMLElement, pathnameRaw: string) { + $$('.current', nav).forEach((el) => el.classList.remove('current')) + + const pathname = stripTrailingSlashForNavHref(pathnameRaw) + $$(`a[href="${pathname}"], a[href="${pathname}/"]`, nav).forEach((el) => + el.classList.add('current') + ) +} + +/** + * Mark the current page's nav link with the "current" CSS class. + * Skips marking when the current page is the section root URL. + */ +function markCurrentPage(nav: HTMLElement) { + if (isOnSectionRootPage(nav)) { + $$('.current', nav).forEach((el) => el.classList.remove('current')) + return + } + markCurrentPageForPath(nav, window.location.pathname) +} + +function pickDeepestAnchorMatchingPath( + nav: HTMLElement, + pathnameRaw: string +): HTMLElement | null { + const pathname = stripTrailingSlashForNavHref(pathnameRaw) + const matches = nav.querySelectorAll( + `a[href="${pathname}"], a[href="${pathname}/"]` + ) + if (matches.length === 0) { + return null + } + + let best = matches[0] + let bestDepth = navListItemDepthFromAnchor(best, nav) + for (let i = 1; i < matches.length; i++) { + const m = matches[i] + const d = navListItemDepthFromAnchor(m, nav) + if (d > bestDepth) { + bestDepth = d + best = m + } + } + return best +} + +/** + * Expand all ancestor collapsible sections that contain the link for {@code pathnameRaw}. + * Uses the deepest matching anchor when several share the URL. + */ +function expandToCurrentPageForPath(nav: HTMLElement, pathnameRaw: string) { + const link = pickDeepestAnchorMatchingPath(nav, pathnameRaw) + if (!link) { + return + } + + const collapsedIds = readCollapsedFolderIds() + + let el: Element | null = link.parentElement + while (el && el !== nav) { + if (el.matches('li')) { + const cb = el.querySelector( + ':scope > .peer input[type=checkbox]' + ) + if (cb && cb.id) { + const rowLink = el.querySelector( + ':scope > .nav-folder-peer > a.sidebar-link' + ) + const currentIsThisFolderRow = + rowLink !== null && rowLink === link + + if (collapsedIds.has(cb.id)) { + if (currentIsThisFolderRow) { + // User collapsed this folder while its index is current; HTML swap often + // re-checks the input — force closed so a second click can stay collapsed. + cb.checked = false + } else { + collapsedIds.delete(cb.id) + writeCollapsedFolderIds(collapsedIds) + cb.checked = true + } + } else { + cb.checked = true + } + } else if (cb) { + cb.checked = true + } + } + + el = el.parentElement + } +} + +/** + * Expand all ancestor collapsible sections that contain the current page link, + * so that navigating directly to a URL reveals its location in the sidebar. + * Does not re-open a folder row that the user collapsed while that folder index + * is the current page (see session storage + folder row link match). + */ +function expandToCurrentPage(nav: HTMLElement) { + if (isOnSectionRootPage(nav)) { + // On the section root page, expand all top-level folders so the + // section content is visible even though no specific page is current. + nav.querySelectorAll( + '#nav-tree > li > .peer > input[type="checkbox"]' + ).forEach((cb) => { + cb.checked = true + }) + return + } + expandToCurrentPageForPath(nav, window.location.pathname) +} + +function destroyNavV2TruncationTooltips() { + for (const instance of navV2TruncationTippyInstances) { + instance.destroy() + } + + navV2TruncationTippyInstances = [] +} + +function measureNavTextEl(ref: HTMLElement, textEl: HTMLElement) { + return ref === textEl + ? textEl + : (ref.querySelector('.docs-sidebar-nav-v2__nav-text') ?? + textEl) +} + +/** + * Tippy tooltips only when text is truncated (single-line ellipsis). onShow returns false to cancel. + */ +function initNavV2TruncationTooltips(nav: HTMLElement) { + destroyNavV2TruncationTooltips() + + const els = nav.querySelectorAll( + '.docs-sidebar-nav-v2__nav-text' + ) + for (const el of els) { + const full = el.textContent?.trim() ?? '' + if (!full) { + continue + } + + const ref: HTMLElement = + el.parentElement?.matches('a.sidebar-link') === true + ? (el.parentElement as HTMLElement) + : el + + const instance = tippy(ref, { + content: full, + placement: 'right-start', + offset: [0, 6], + animation: 'fade', + duration: [200, 150], + arrow: true, + maxWidth: 360, + appendTo: () => document.body, + theme: 'nav-v2-truncate', + trigger: 'mouseenter focusin', + hideOnClick: true, + interactive: false, + touch: ['hold', 500], + aria: { content: 'describedby' }, + onShow() { + const textEl = measureNavTextEl(ref, el) + const label = textEl.textContent?.trim() ?? '' + if (!label) { + return false + } + + instance.setContent(label) + if (textEl.scrollWidth <= textEl.clientWidth + 1) { + return false + } + }, + }) + + navV2TruncationTippyInstances.push(instance) + } +} + +/** + * Initialize all V2 nav behaviours on the given sidebar element. + * Call this on every htmx:load when [data-nav-v2] is present. + */ +export function initNavV2(nav: HTMLElement) { + initAccordion(nav) + markCurrentPage(nav) + expandToCurrentPage(nav) + applyActiveSubtreeHighlight(nav) + initNavV2FolderLayoutWarmup(nav) + requestAnimationFrame(() => { + requestAnimationFrame(() => initNavV2TruncationTooltips(nav)) + }) +} + +ensureNavV2FolderLinkToggle() +ensureNavV2OptimisticCurrentOnNavigate() diff --git a/src/Elastic.Documentation.Site/Assets/secondary-nav-dropdown.css b/src/Elastic.Documentation.Site/Assets/secondary-nav-dropdown.css new file mode 100644 index 0000000000..ea82bd88da --- /dev/null +++ b/src/Elastic.Documentation.Site/Assets/secondary-nav-dropdown.css @@ -0,0 +1,102 @@ +/* + * Top-bar section dropdown. Opt in by setting `dropdown: true` on a + * `- section:` entry in `navigation-v2.yml`. The section's own URL + * remains the link target on the summary's anchor; the chevron toggles + * a panel listing the section's children so users can jump straight + * to a child page without going through the section landing. + */ + +@layer components { + .secondary-nav-dropdown { + display: inline-flex; + align-items: center; + } + .secondary-nav-dropdown summary { + list-style: none; + } + .secondary-nav-dropdown summary::-webkit-details-marker { + display: none; + } + + .secondary-nav-dropdown-chevron { + flex-shrink: 0; + color: var(--color-grey-60); + transition: transform 0.15s ease; + } + .secondary-nav-dropdown[open] .secondary-nav-dropdown-chevron { + transform: rotate(180deg); + } + + .secondary-nav-dropdown-menu { + position: absolute; + top: calc(100% + 4px); + left: 0; + min-width: 220px; + max-width: 320px; + z-index: 50; + display: flex; + flex-direction: column; + padding: 6px 0; + background: var(--color-white); + border: 1px solid var(--color-grey-20); + border-radius: 6px; + box-shadow: 0 8px 24px rgb(0 0 0 / 0.08); + font-weight: 500; + } + + .secondary-nav-dropdown-group-label { + padding: 8px 14px 4px 14px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--color-grey-70); + user-select: none; + } + .secondary-nav-dropdown-group-label:not(:first-child) { + margin-top: 4px; + border-top: 1px solid var(--color-grey-15, var(--color-grey-20)); + padding-top: 10px; + } + + .secondary-nav-dropdown-link { + display: block; + padding: 6px 14px; + font-size: 14px; + color: var(--color-ink-dark) !important; + text-decoration: none !important; + transition: background-color 0.12s ease; + } + .secondary-nav-dropdown-link:hover { + background: var(--color-grey-10); + color: var(--color-blue-elastic) !important; + } + + .secondary-nav-dropdown-stub { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 14px; + font-size: 14px; + color: var(--color-grey-60); + cursor: default; + } + .secondary-nav-dropdown-stub::after { + content: 'soon'; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + padding: 2px 6px; + border-radius: 4px; + background: var(--color-grey-15, var(--color-grey-20)); + color: var(--color-grey-70); + } + + /* Close the menu when the user clicks outside is browser-default for +
    ; just suppress the htmx-boost on summary clicks so the + first click only toggles. */ + .secondary-nav-dropdown summary a { + text-decoration: none; + } +} diff --git a/src/Elastic.Documentation.Site/Assets/styles.css b/src/Elastic.Documentation.Site/Assets/styles.css index 3d3edd26d9..039ffc2ceb 100644 --- a/src/Elastic.Documentation.Site/Assets/styles.css +++ b/src/Elastic.Documentation.Site/Assets/styles.css @@ -27,6 +27,9 @@ @import './markdown/stepper.css'; @import './markdown/button.css'; @import './markdown/agent-skill.css'; +@import './markdown/hub.css'; +@import './markdown/metadata-box.css'; +@import './secondary-nav-dropdown.css'; @import './markdown/contributors.css'; @import './api-docs.css'; @import 'tippy.js/dist/tippy.css'; @@ -109,7 +112,20 @@ body { } .sidebar { - .sidebar-nav { + /* Pages nav: column layout; scroll lives in .pages-nav-menu (see _TocTree.cshtml). */ + #pages-nav.sidebar-nav { + @apply sticky top-(--offset-top); + display: flex; + flex-direction: column; + min-height: 0; + max-height: calc(100vh - var(--offset-top)); + overflow: hidden; + transition: + top 0.3s ease, + max-height 0.3s ease; + } + + .sidebar-nav:not(#pages-nav) { @apply sticky top-(--offset-top) overflow-y-auto; max-height: calc(100vh - var(--offset-top)); scrollbar-gutter: stable; @@ -118,12 +134,489 @@ body { max-height 0.3s ease; } + /* Shared by V1 and V2 base. Hover ink: V1 pages tree + non-pages sidebars only — Nav V2 keeps color on hover (see #pages-nav nav[data-nav-v2]). */ .sidebar-link { - @apply text-ink-light inline-block leading-[1.2em] text-pretty hover:text-black md:text-sm; + @apply text-ink-light inline-block text-[14px] leading-[20px] text-pretty; word-break: break-word; } + + #pages-nav .pages-nav-menu > ul .sidebar-link:hover, + .sidebar-nav:not(#pages-nav) .sidebar-link:hover { + @apply text-black; + } + + /* In-page TOC: padding on li; link fills row for full-width hit target. */ + & #toc-nav .toc-progress-container a.sidebar-link { + display: block; + width: 100%; + box-sizing: border-box; + } + } +} + +/* Assembler nav V2: layout chrome (sidebar width). Border: default from aside (border-r-grey-20). */ +@media (width >= 768px) { + body:has([data-nav-v2]) div.min-h-screen.grid { + grid-template-columns: minmax(0, 280px) 1fr; + } + + body:has([data-nav-v2]) + div.grid.w-full.h-full:has(> aside.sidebar:first-child) { + grid-template-columns: minmax(0, 280px) 1fr; + } +} + +body:has([data-nav-v2]) aside.sidebar { + max-width: 280px; +} + +/* Nav V2: no motion on sidebar column (sticky offset / max-height). */ +#pages-nav:has(nav[data-nav-v2]).sidebar-nav { + transition: none; +} + +/* Sticky header: hairline under search row when nav list is scrolled. */ +#pages-nav #pages-nav-sticky-chrome { + border-bottom: 1px solid transparent; + transition: none; +} + +#pages-nav #pages-nav-sticky-chrome[data-nav-scrolled] { + border-bottom-color: #e3e8f2; +} + +.sidebar #pages-nav .pages-nav-menu { + scrollbar-gutter: stable; + padding-right: 8px; +} + +/* Nav V2: stack list items vertically */ +#pages-nav nav[data-nav-v2] ul { + display: flex; + flex-direction: column; + list-style: none; +} + +/* + * Subsection lists (see NavV2LabelListKind + _TocTreeNavV2): ul under a label when children are + * nested labels / links only — not li.group-navigation rows. Non-subsection uses .docs-sidebar-nav-v2__label-children. + */ + +/* No gap between rows inside a subsection list (links under a nested subtitle). */ +#pages-nav + nav[data-nav-v2] + li + > .docs-sidebar-nav-v2__label--nested + + ul.docs-sidebar-nav-v2__subsection { + gap: 0 !important; +} + +/* Gap between sibling subsection blocks (each li: nested subtitle + its ul) under a top icon section only. */ +#pages-nav + nav[data-nav-v2] + li + > .docs-sidebar-nav-v2__label--top + + ul.docs-sidebar-nav-v2__subsection { + gap: 0 !important; +} + +#pages-nav + nav[data-nav-v2] + li + > .docs-sidebar-nav-v2__label--top + + ul.docs-sidebar-nav-v2__subsection:has( + > li > .docs-sidebar-nav-v2__label--nested + ) { + gap: 0.5rem !important; +} + +/* + * Folder groups: CSS Grid 0fr → 1fr (no scrollHeight in JS). The engine interpolates the row + * track instead of measuring pixel height on the main thread (avoids first-open jank). + */ +#pages-nav + nav[data-nav-v2] + li.group-navigation + > .docs-sidebar-nav-v2__folder-clip { + display: grid; + grid-template-rows: 0fr; + transition: grid-template-rows 220ms cubic-bezier(0.25, 0.1, 0.25, 1); +} + +/* Hint row-track interpolation while the pointer is on this folder (cheap vs permanent will-change on every row). */ +#pages-nav + nav[data-nav-v2] + li.group-navigation:hover + > .docs-sidebar-nav-v2__folder-clip { + will-change: grid-template-rows; +} + +/* Only this folder's checkbox opens its clip — :has() must not match nested descendants' peers or ancestor lis repeat-expand (duplicate-looking children). */ +#pages-nav + nav[data-nav-v2] + li.group-navigation:has(> .nav-folder-peer input[type='checkbox']:checked) + > .docs-sidebar-nav-v2__folder-clip { + grid-template-rows: 1fr; +} + +#pages-nav nav[data-nav-v2] .docs-sidebar-nav-v2__folder-clip-inner { + min-height: 0; + overflow: hidden; + transform: translateZ(0); +} + +/* + * Folder accordion body: flat/styled link rows (chevron folders, leaves). Distinct from + * .docs-sidebar-nav-v2__subsection (label heading + list pattern). + */ +#pages-nav nav[data-nav-v2] ul.docs-sidebar-nav-v2__folder-children { + display: flex; + flex-direction: column; + list-style: none; +} + +@media (prefers-reduced-motion: reduce) { + #pages-nav + nav[data-nav-v2] + li.group-navigation + > .docs-sidebar-nav-v2__folder-clip { + transition: none; } +} + +/* + * Nav V2: block spacing only for root rows that start a top-level section (label--top). + * Leaf / folder rows without a top label do not get the 24px bottom padding. + */ +#pages-nav + nav[data-nav-v2] + ul.docs-sidebar-nav-v2__tree + > li:has(> .docs-sidebar-nav-v2__label--top) { + margin-top: 0; + padding: 0 0 24px 0; +} + +/* Breathing room when sticky chrome is empty (no search, no product dropdown). */ +#pages-nav + nav[data-nav-v2] + ul.docs-sidebar-nav-v2__tree + > li:first-child:has(> .docs-sidebar-nav-v2__label--top) { + padding-top: 12px; +} + +/* Search / dropdown already add top chrome; drop extra padding on first tree row. */ +#pages-nav + #pages-nav-sticky-chrome:has(navigation-search) + + .pages-nav-menu + nav[data-nav-v2] + ul.docs-sidebar-nav-v2__tree + > li:first-child:has(> .docs-sidebar-nav-v2__label--top), +#pages-nav + #pages-nav-sticky-chrome:has(#pages-dropdown) + + .pages-nav-menu + nav[data-nav-v2] + ul.docs-sidebar-nav-v2__tree + > li:first-child:has(> .docs-sidebar-nav-v2__label--top) { + padding-top: 0; +} + +/* Tippy mounts inside nav; clip tooltips so nothing paints outside the sidebar column */ +nav.docs-sidebar-nav-v2 { + position: relative; + overflow: hidden; +} + +/* Nav V2: top-level section titles (with icon) — inherit body font stack */ +nav.docs-sidebar-nav-v2 .docs-sidebar-nav-v2__label--top { + font-weight: 600; + margin: 12px 0 16px 0; +} + +/* + * Label--top spacing: + * - Default (12px 0 16px): top sections whose body is a folder list (.docs-sidebar-nav-v2__label-children). + * - Tighter bottom (12px 0 8px): top sections whose body is a true subsection list (nested labels only). + */ +#pages-nav + nav[data-nav-v2] + .docs-sidebar-nav-v2__label--top:has(+ ul.docs-sidebar-nav-v2__subsection) { + margin: 12px 0 8px 0; +} + +/* Nav V2: nested subgroup section titles (non-clickable; color never overridden by active path) */ +nav.docs-sidebar-nav-v2 .docs-sidebar-nav-v2__label--nested { + font-weight: 500; + margin: 16px 0; + color: #a2b1c9; +} + +/* Nav V2: top-level section title text only (icons removed). */ +nav.docs-sidebar-nav-v2 + .docs-sidebar-nav-v2__label--top + .docs-sidebar-nav-v2__label-icon { + display: none; + width: 16px; + height: 16px; + flex-shrink: 0; + color: #111c2c; +} + +/* Nav V2: flex column needs min-width: 0 on li; labels wrap. */ +#pages-nav nav[data-nav-v2] li { + min-width: 0; + width: 100%; +} + +#pages-nav nav[data-nav-v2] .docs-sidebar-nav-v2__nav-text { + min-width: 0; + max-width: 100%; + white-space: normal; + word-break: break-word; + overflow-wrap: break-word; +} + +#pages-nav + nav[data-nav-v2] + .docs-sidebar-nav-v2__label--top + .docs-sidebar-nav-v2__nav-text { + flex: 1 1 0%; +} + +/* Folder rows: text wraps; chevron + label vertically centered (Tailwind items-center; unlayered override kept explicit) */ +#pages-nav nav[data-nav-v2] a.sidebar-link.flex { + min-width: 0; + align-items: center; +} +/* Nav V2: default link ink; placeholders keep .text-grey-40. Current row follows EUI-like selected state. */ +#pages-nav nav[data-nav-v2] a.sidebar-link { + box-sizing: border-box; + width: 100%; + max-width: 100%; + min-width: 0; + text-wrap: wrap; + font-weight: 400; + border-radius: 8px; + background-color: transparent; +} + +#pages-nav nav[data-nav-v2] a.sidebar-link.nav-v2-link { + padding-inline-end: 10px; + padding-inline-start: max(10px, calc(10px + var(--nav-level, 0) * 12px)); +} + +#pages-nav nav[data-nav-v2] a.sidebar-link:not(.text-grey-40) { + color: #516381; +} + +#pages-nav + nav[data-nav-v2] + a.sidebar-link:not(.text-grey-40):not(.current):hover { + background-color: #f6f9fc; +} + +#pages-nav nav[data-nav-v2] a.sidebar-link.text-grey-40 { + color: var(--color-grey-40); +} + +#pages-nav nav[data-nav-v2] a.sidebar-link.text-grey-40:not(.current):hover { + background-color: #f6f9fc; +} + +#pages-nav nav[data-nav-v2] a.sidebar-link.current { + position: relative; + color: #0b64dd !important; + font-weight: 600 !important; + background-color: #f6f9fc !important; +} + +#pages-nav nav[data-nav-v2] a.sidebar-link.current:hover { + color: #0b64dd; + background-color: #f6f9fc !important; +} + +/* Vertical accent on the active page row (blue bar + blue title; chevron uses parent-path ink). */ +#pages-nav nav[data-nav-v2] a.sidebar-link.current::before { + content: ''; + position: absolute; + inset-inline-start: 1px; + top: 50%; + transform: translateY(-50%); + width: 3px; + height: 1.125rem; + max-height: calc(100% - 10px); + border-radius: 9999px; + background-color: var(--color-blue-elastic); + pointer-events: none; + z-index: 1; +} + +/* Nav V2: active section background — subtree vs leaf driven by pages-nav-v2.ts */ +#pages-nav nav[data-nav-v2] li.nav-v2-active-subtree { + background-color: transparent; + border-radius: 8px; +} + +#pages-nav nav[data-nav-v2] li.nav-v2-active-leaf { + background-color: transparent; + border-radius: 8px; +} + +#pages-nav nav[data-nav-v2] li.nav-v2-active-subtree a.sidebar-link, +#pages-nav nav[data-nav-v2] li.nav-v2-active-leaf > a.sidebar-link { + background-color: transparent; +} + +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-subtree + a.sidebar-link:not(.current):hover { + background-color: #f6f9fc !important; +} + +#pages-nav nav[data-nav-v2] li.nav-v2-active-subtree a.sidebar-link.current, +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-subtree + a.sidebar-link.current:hover { + background-color: #f6f9fc !important; +} + +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-leaf + > a.sidebar-link:not(.current):hover { + background-color: #f6f9fc !important; +} + +#pages-nav nav[data-nav-v2] li.nav-v2-active-leaf > a.sidebar-link.current, +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-leaf + > a.sidebar-link.current:hover { + background-color: #f6f9fc !important; +} + +/* + * Ancestor folder rows (current is deeper): semibold + dark ink on the open path. + * .current on the same row (folder index) uses blue text; chevron stays dark via scoped rule below. + */ +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-ancestor + > .nav-folder-peer + > a.sidebar-link { + color: #1d2a3e !important; + font-weight: 600 !important; +} + +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-ancestor + > .nav-folder-peer + > a.sidebar-link.current { + color: #0b64dd !important; + font-weight: 600 !important; +} + +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-ancestor + > .nav-folder-peer + > a.sidebar-link.text-grey-40 { + color: var(--color-grey-40) !important; + font-weight: 600 !important; +} + +#pages-nav nav[data-nav-v2] li > a.sidebar-link, +#pages-nav nav[data-nav-v2] li > .nav-folder-peer > a.sidebar-link { + margin-bottom: 1px; +} + +#pages-nav nav[data-nav-v2] li:last-child > a.sidebar-link, +#pages-nav nav[data-nav-v2] li:last-child > .nav-folder-peer > a.sidebar-link { + margin-bottom: 0; +} + +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-ancestor + > .nav-folder-peer + > a.sidebar-link:not(.current):hover { + background-color: #f6f9fc; +} + +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-ancestor + > .nav-folder-peer + > a.sidebar-link.current:hover { + color: #0b64dd !important; +} + +#pages-nav nav[data-nav-v2] .nav-folder-peer { + min-width: 0; +} + +/* Chevrons: muted by default; active folder row keeps parent-path ink on the icon, not link blue. */ +#pages-nav nav[data-nav-v2] .nav-folder-chevron { + color: #a2b1c9; +} + +#pages-nav + nav[data-nav-v2] + .nav-folder-peer + > a.sidebar-link.current + .nav-folder-chevron { + color: #1d2a3e; +} + +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-ancestor + > .nav-folder-peer + > a.sidebar-link:not(.current) + .nav-folder-chevron { + color: currentColor; +} + +#pages-nav + nav[data-nav-v2] + .nav-folder-peer + > a.sidebar-link.current + .nav-folder-chevron + svg, +#pages-nav + nav[data-nav-v2] + li.nav-v2-active-ancestor + > .nav-folder-peer + > a.sidebar-link + .nav-folder-chevron + svg { + stroke-width: 3.5; +} + +.docs-sidebar-nav-v2 .nav-folder-peer .nav-folder-chevron svg { + display: block; + width: 12px; + height: 12px; + transform: rotate(-90deg); +} + +.docs-sidebar-nav-v2 .nav-item-slot { + display: block; + width: 12px; + height: 12px; +} + +.docs-sidebar-nav-v2 + .nav-folder-peer:has(input[type='checkbox']:checked) + .nav-folder-chevron + svg { + transform: rotate(0deg); +} + +@layer components { .sidebar-trial-card { background: var(--color-green-30); border-radius: 8px; @@ -358,7 +851,8 @@ body { ); } -#pages-nav .current { +/* V1 toc only: V2 current styling is #pages-nav nav[data-nav-v2] a.sidebar-link.current */ +#pages-nav .current:not(nav.docs-sidebar-nav-v2 a.sidebar-link) { @apply text-blue-elastic! font-semibold; } @@ -418,7 +912,7 @@ math { * { scrollbar-width: thin; - scrollbar-color: rgba(113, 134, 168, 0.5) transparent; + scrollbar-color: #e3e8f2 transparent; } /* Heading anchor links — show # on hover */ diff --git a/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/NavigationSearchComponent.tsx b/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/NavigationSearchComponent.tsx index 38ed77cd31..351bf07e66 100644 --- a/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/NavigationSearchComponent.tsx +++ b/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/NavigationSearchComponent.tsx @@ -2,7 +2,7 @@ import { config } from '../../config' import '../../eui-icons-cache' import { sharedQueryClient } from '../shared/queryClient' import { NavigationSearch } from './NavigationSearch' -import { EuiHorizontalRule, EuiProvider, useEuiTheme } from '@elastic/eui' +import { EuiProvider } from '@elastic/eui' import { css } from '@emotion/react' import r2wc from '@r2wc/react-to-web-component' import { QueryClientProvider, useQuery } from '@tanstack/react-query' @@ -13,7 +13,6 @@ interface NavigationSearchProps { } const NavigationSearchInner = ({ placeholder }: NavigationSearchProps) => { - const { euiTheme } = useEuiTheme() const { data: isApiAvailable } = useQuery({ queryKey: ['api-health'], queryFn: async () => { @@ -35,17 +34,13 @@ const NavigationSearchInner = ({ placeholder }: NavigationSearchProps) => {
    -
    ) } diff --git a/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/SearchInput.tsx b/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/SearchInput.tsx index 607d30d988..f2ceedb077 100644 --- a/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/SearchInput.tsx +++ b/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/SearchInput.tsx @@ -11,9 +11,10 @@ const useIsMac = () => { }, []) } -const CustomSearchIcon = () => { - const { euiTheme } = useEuiTheme() +const navSearchMuted = '#cad3e2' +const navSearchBorder = '#e3e8f2' +const CustomSearchIcon = () => { return ( { fill="none" xmlns="http://www.w3.org/2000/svg" css={css` - color: ${euiTheme.colors.textDisabled}; + color: ${navSearchMuted}; flex-shrink: 0; `} > @@ -80,7 +81,14 @@ export const SearchInput = ({ `} > {isLoading ? ( - + + + ) : ( )} @@ -110,20 +118,27 @@ export const SearchInput = ({ ${euiTheme.size.m} + ${isMac ? '2ch' : '4ch'} + ${euiTheme.size.m} ); - border: 1px solid ${euiTheme.colors.borderBasePlain}; - border-radius: ${euiTheme.border.radius.medium}; - background: ${euiTheme.colors.backgroundBaseSubdued}; + border: 1px solid ${navSearchBorder}; + border-radius: 8px; + background: #ffffff; font-size: ${euiTheme.font.scale.s * euiTheme.base}px; line-height: ${euiTheme.base * 1.25}px; color: ${euiTheme.colors.textParagraph}; outline: none; + box-shadow: none; &::placeholder { - color: ${euiTheme.colors.textDisabled}; + color: ${navSearchMuted}; + } + + &:focus:not(:focus-visible) { + border-color: ${navSearchBorder}; } - &:focus { - border-color: ${euiTheme.colors.primary}; + &:focus-visible { + border: 1px solid ${navSearchMuted}; + outline: none; + box-shadow: none; } `} /> @@ -135,7 +150,7 @@ export const SearchInput = ({ display: inline-flex; align-items: center; pointer-events: none; - color: ${euiTheme.colors.textDisabled}; + color: ${navSearchMuted}; font-size: ${euiTheme.font.scale.s * euiTheme.base}px; line-height: ${euiTheme.base * 1.25}px; `} diff --git a/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/SearchResultsList.tsx b/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/SearchResultsList.tsx index 4424e58b04..c6e2a06393 100644 --- a/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/SearchResultsList.tsx +++ b/src/Elastic.Documentation.Site/Assets/web-components/NavigationSearch/SearchResultsList.tsx @@ -176,8 +176,8 @@ const SearchResultRow = ({ display: flex; align-items: center; gap: ${euiTheme.size.s}; - padding-inline: ${euiTheme.size.l}; - padding-block: ${euiTheme.size.base}; + padding-inline: 8px; + padding-block: 6px; text-decoration: none; cursor: pointer; border-bottom: 1px solid ${euiTheme.colors.borderBaseSubdued}; diff --git a/src/Elastic.Documentation.Site/Assets/web-components/shared/htmx/utils.ts b/src/Elastic.Documentation.Site/Assets/web-components/shared/htmx/utils.ts index 6caba2af75..3ecdc23a26 100644 --- a/src/Elastic.Documentation.Site/Assets/web-components/shared/htmx/utils.ts +++ b/src/Elastic.Documentation.Site/Assets/web-components/shared/htmx/utils.ts @@ -19,7 +19,7 @@ const getHxSelectOob = (targetUrl: string, currentPathname: string): string => { const base = currentSegment === targetSegment ? '#content-container,#toc-nav' - : '#content-container,#toc-nav,#nav-tree,#nav-dropdown' + : '#content-container,#toc-nav,#pages-nav' return config.buildType === 'codex' ? `${base},#codex-breadcrumbs` : base } @@ -29,7 +29,7 @@ const getHxSelectOob = (targetUrl: string, currentPathname: string): string => { * * - For simple swap paths (landing, /docs/api/*, /g/*): swap only #main-container * - For same top-level group: swap #content-container,#toc-nav - * - For different top-level group: swap #content-container,#toc-nav,#nav-tree,#nav-dropdown + * - For different top-level group: swap #content-container,#toc-nav,#pages-nav */ export const applyHtmxAttributes = ( anchor: HTMLAnchorElement, diff --git a/src/Elastic.Documentation.Site/FileProviders/StaticFileContentHashProvider.cs b/src/Elastic.Documentation.Site/FileProviders/StaticFileContentHashProvider.cs index 20a995bd6d..8b8b9bf777 100644 --- a/src/Elastic.Documentation.Site/FileProviders/StaticFileContentHashProvider.cs +++ b/src/Elastic.Documentation.Site/FileProviders/StaticFileContentHashProvider.cs @@ -8,22 +8,23 @@ namespace Elastic.Documentation.Site.FileProviders; public class StaticFileContentHashProvider(EmbeddedOrPhysicalFileProvider fileProvider) { - private readonly ConcurrentDictionary _contentHashes = []; + private readonly ConcurrentDictionary _contentHashes = []; public string GetContentHash(string path) { - if (_contentHashes.TryGetValue(path, out var contentHash)) - return contentHash; - var fileInfo = fileProvider.GetFileInfo(path); if (!fileInfo.Exists) return string.Empty; + if (_contentHashes.TryGetValue(path, out var cached) && cached.LastModified == fileInfo.LastModified) + return cached.Hash; + using var stream = fileInfo.CreateReadStream(); using var sha = System.Security.Cryptography.SHA256.Create(); var fullHash = sha.ComputeHash(stream); - _contentHashes[path] = Convert.ToHexString(fullHash).ToLowerInvariant()[..16]; - return _contentHashes[path]; + var hash = Convert.ToHexString(fullHash).ToLowerInvariant()[..16]; + _contentHashes[path] = (hash, fileInfo.LastModified); + return hash; } } diff --git a/src/Elastic.Documentation.Site/Htmx.cs b/src/Elastic.Documentation.Site/Htmx.cs index b4fe31adf2..f460f899e0 100644 --- a/src/Elastic.Documentation.Site/Htmx.cs +++ b/src/Elastic.Documentation.Site/Htmx.cs @@ -16,7 +16,7 @@ public class DefaultHtmxAttributeProvider(string rootPath) : IHtmxAttributeProvi public virtual string GetHxSelectOob(bool hasSameTopLevelGroup) => hasSameTopLevelGroup ? "#content-container,#toc-nav" - : "#content-container,#toc-nav,#nav-tree,#nav-dropdown"; + : "#content-container,#toc-nav,#pages-nav"; public string GetHxAttributes( bool hasSameTopLevelGroup = false, @@ -26,7 +26,8 @@ public string GetHxAttributes( { var attributes = new StringBuilder(); _ = attributes.Append($" hx-select-oob={hxSwapOob ?? GetHxSelectOob(hasSameTopLevelGroup)}"); - _ = attributes.Append($" preload={preload}"); + if (!string.IsNullOrEmpty(preload)) + _ = attributes.Append($" preload={preload}"); return attributes.ToString(); } @@ -34,7 +35,8 @@ public string GetNavHxAttributes(bool hasSameTopLevelGroup = false, string? prel { var attributes = new StringBuilder(); _ = attributes.Append($" hx-select-oob={GetHxSelectOob(hasSameTopLevelGroup)}"); - _ = attributes.Append($" preload={preload}"); + if (!string.IsNullOrEmpty(preload)) + _ = attributes.Append($" preload={preload}"); return attributes.ToString(); } } diff --git a/src/Elastic.Documentation.Site/IHtmxAttributeProvider.cs b/src/Elastic.Documentation.Site/IHtmxAttributeProvider.cs index 9924a221ee..657a52e1da 100644 --- a/src/Elastic.Documentation.Site/IHtmxAttributeProvider.cs +++ b/src/Elastic.Documentation.Site/IHtmxAttributeProvider.cs @@ -14,8 +14,10 @@ public interface IHtmxAttributeProvider string GetHxSelectOob(bool hasSameTopLevelGroup); /// Gets HTMX attributes for a link. + /// htmx-ext-preload trigger (e.g. mousedown). Null or empty omits preload (avoids duplicate GET with the real navigation request). string GetHxAttributes(bool hasSameTopLevelGroup = false, string? preload = "mousedown", string? hxSwapOob = null); /// Gets HTMX attributes for navigation links. + /// Same as ; pass null for sidebar V2 links to disable preload. string GetNavHxAttributes(bool hasSameTopLevelGroup = false, string? preload = "mousedown"); } diff --git a/src/Elastic.Documentation.Site/Layout/_SecondaryNav.cshtml b/src/Elastic.Documentation.Site/Layout/_SecondaryNav.cshtml index 84f03e90fd..0f9dea550c 100644 --- a/src/Elastic.Documentation.Site/Layout/_SecondaryNav.cshtml +++ b/src/Elastic.Documentation.Site/Layout/_SecondaryNav.cshtml @@ -1,48 +1,134 @@ +@using System.Linq +@using Elastic.Documentation.Navigation +@using Elastic.Documentation.Navigation.V2 @inherits RazorSlice
    + diff --git a/src/Elastic.Documentation.Site/Navigation/INavigationHtmlWriter.cs b/src/Elastic.Documentation.Site/Navigation/INavigationHtmlWriter.cs index c26a89a674..6d73feccbf 100644 --- a/src/Elastic.Documentation.Site/Navigation/INavigationHtmlWriter.cs +++ b/src/Elastic.Documentation.Site/Navigation/INavigationHtmlWriter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.Navigation; +using Elastic.Documentation.Navigation.V2; using RazorSlices; namespace Elastic.Documentation.Site.Navigation; @@ -26,9 +27,15 @@ public record NavigationRenderResult public static NavigationRenderResult Empty { get; } = new() { Html = string.Empty, - Id = "empty-navigation" // random id + Id = "empty-navigation" }; public required string Html { get; init; } public required string Id { get; init; } + + /// V2 section metadata for the secondary nav bar. Null for V1 builds. + public IReadOnlyList? Sections { get; init; } + + /// The active section ID for highlighting the current tab. + public string? ActiveSectionId { get; init; } } diff --git a/src/Elastic.Documentation.Site/Navigation/IsolatedBuildNavigationHtmlWriter.cs b/src/Elastic.Documentation.Site/Navigation/IsolatedBuildNavigationHtmlWriter.cs index d9dce74edc..9254bc9458 100644 --- a/src/Elastic.Documentation.Site/Navigation/IsolatedBuildNavigationHtmlWriter.cs +++ b/src/Elastic.Documentation.Site/Navigation/IsolatedBuildNavigationHtmlWriter.cs @@ -73,7 +73,9 @@ private NavigationViewModel CreateNavigationModel(IRootNavigationItem>().ToList(), Htmx = htmx, BuildType = context.BuildType, - Branding = context.Configuration.Branding + Branding = context.Configuration.Branding, + // Isolated serve (e.g. port 3000): reuse V2 sidebar markup/CSS/JS for local styling; tree is still docset TOC only. + IsNavV2 = context.BuildType == BuildType.Isolated }; } diff --git a/src/Elastic.Documentation.Site/Navigation/NavV2LabelListKind.cs b/src/Elastic.Documentation.Site/Navigation/NavV2LabelListKind.cs new file mode 100644 index 0000000000..4665099182 --- /dev/null +++ b/src/Elastic.Documentation.Site/Navigation/NavV2LabelListKind.cs @@ -0,0 +1,30 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Documentation.Navigation; +using Elastic.Documentation.Navigation.V2; + +namespace Elastic.Documentation.Site.Navigation; + +internal static class NavV2LabelListKind +{ + /// + /// True when every direct child under this label renders as a non-folder row (nested labels, plain links). + /// False when any child renders as li.group-navigation (accordion folder rows from _TocTreeNavV2). + /// + public static bool IsSubsectionList(LabelNavigationNode label) => + label.NavigationItems.Count > 0 && label.NavigationItems.All(i => !RendersAsGroupNavigationRow(i)); + + private static bool RendersAsGroupNavigationRow(INavigationItem item) + { + if (item is PlaceholderNavigationNode) + return true; + if (item is LabelNavigationNode) + return false; + if (item is INodeNavigationItem node && node.NavigationItems.Count > 0) + return true; + + return false; + } +} diff --git a/src/Elastic.Documentation.Site/Navigation/NavigationViewModel.cs b/src/Elastic.Documentation.Site/Navigation/NavigationViewModel.cs index b241f797fc..aeff462d33 100644 --- a/src/Elastic.Documentation.Site/Navigation/NavigationViewModel.cs +++ b/src/Elastic.Documentation.Site/Navigation/NavigationViewModel.cs @@ -26,4 +26,16 @@ public class NavigationViewModel public BuildType BuildType { get; init; } = BuildType.Isolated; public BrandingConfiguration? Branding { get; init; } + + /// When true, the sidebar renders using the V2 nav partial with accordion behaviour. + public bool IsNavV2 { get; init; } + + /// When true, the sidebar renders a back arrow instead of appearing in the top bar. + public bool IsIsolatedSection { get; init; } + + /// The section's own URL, used by JS to skip current-page highlighting on the section root. + public string? SectionUrl { get; init; } + + /// Back arrow URL for islands — points to the parent section's landing page. + public string? BackArrowUrl { get; init; } } diff --git a/src/Elastic.Documentation.Site/Navigation/_TocTree.cshtml b/src/Elastic.Documentation.Site/Navigation/_TocTree.cshtml index 3a83d7b8b6..f0dd790e5b 100644 --- a/src/Elastic.Documentation.Site/Navigation/_TocTree.cshtml +++ b/src/Elastic.Documentation.Site/Navigation/_TocTree.cshtml @@ -1,12 +1,13 @@ @using Elastic.Documentation @using Elastic.Documentation.Site.Navigation +@using Elastic.Documentation.Navigation @inherits RazorSlice -
    +
    @{ var currentTopLevelItem = Model.TopLevelItems.FirstOrDefault(i => i.Id == Model.Tree.Id) ?? Model.Tree; } -
    +
    @if (Model.BuildType != BuildType.Codex && Model.Branding is null) { @@ -19,7 +20,7 @@
    - + + + Back + + } + @* Root for V2-only CSS; JS still uses [data-nav-v2]. Scope overrides: .docs-sidebar-nav-v2 … *@ + + } + else + { + + } +
    diff --git a/src/Elastic.Documentation.Site/Navigation/_TocTreeNav.cshtml b/src/Elastic.Documentation.Site/Navigation/_TocTreeNav.cshtml index cbd1dbfaa5..547eb082e6 100644 --- a/src/Elastic.Documentation.Site/Navigation/_TocTreeNav.cshtml +++ b/src/Elastic.Documentation.Site/Navigation/_TocTreeNav.cshtml @@ -51,7 +51,7 @@ @groupLabel @if (groupBadge == "ns") { ns } @@ -65,11 +65,11 @@ var allHidden = folder.NavigationItems.All(n => n.Hidden); var (folderBadge, folderLabel) = ParseNavTitle(g.NavigationTitle);
  • -
  • + @if (isTopLevel) + { + + @label.NavigationTitle + + } + else + { + + @label.NavigationTitle + + } + @if (label.NavigationItems.Count > 0) + { +
      + @await RenderPartialAsync(_TocTreeNavV2.Create(new NavigationTreeItem + { + IsPrimaryNavEnabled = Model.IsPrimaryNavEnabled, + IsGlobalAssemblyBuild = Model.IsGlobalAssemblyBuild, + Level = Model.Level, + SubTree = label, + RootNavigationId = Model.RootNavigationId, + Htmx = Model.Htmx + })) +
    + } +
  • + } + else if (item is PlaceholderNavigationLeaf placeholder) + { +
  • + + + @placeholder.NavigationTitle + +
  • + } + else if (item is PlaceholderNavigationNode placeholderGroup) + { + var allHidden = placeholderGroup.NavigationItems.All(n => n.Hidden); +
  • + + @if (placeholderGroup.NavigationItems.Count > 0) + { +
    +
    +
      + @await RenderPartialAsync(_TocTreeNavV2.Create(new NavigationTreeItem + { + IsPrimaryNavEnabled = Model.IsPrimaryNavEnabled, + IsGlobalAssemblyBuild = Model.IsGlobalAssemblyBuild, + Level = Model.Level + 1, + SubTree = placeholderGroup, + RootNavigationId = Model.RootNavigationId, + Htmx = Model.Htmx + })) +
    +
    +
    + } +
  • + } + else if (item is INodeNavigationItem { NavigationItems.Count: 0 } group) + { +
  • + + + @group.NavigationTitle + +
  • + } + else if (item is INodeNavigationItem folder) + { + var allHidden = folder.NavigationItems.All(n => n.Hidden); +
  • + + @if (folder.NavigationItems.Count > 0) + { +
    +
    +
      + @await RenderPartialAsync(_TocTreeNavV2.Create(new NavigationTreeItem + { + IsPrimaryNavEnabled = Model.IsPrimaryNavEnabled, + IsGlobalAssemblyBuild = Model.IsGlobalAssemblyBuild, + Level = Model.Level + 1, + SubTree = folder, + RootNavigationId = Model.RootNavigationId, + Htmx = Model.Htmx + })) +
    +
    +
    + } +
  • + } + else if (item is ILeafNavigationItem leaf) + { +
  • + + + @leaf.NavigationTitle + +
  • + } +} diff --git a/src/Elastic.Documentation.Site/_ViewModels.cs b/src/Elastic.Documentation.Site/_ViewModels.cs index 6a9245b9f1..1824835e0c 100644 --- a/src/Elastic.Documentation.Site/_ViewModels.cs +++ b/src/Elastic.Documentation.Site/_ViewModels.cs @@ -11,6 +11,7 @@ using Elastic.Documentation.Configuration.Builder; using Elastic.Documentation.Configuration.Toc; using Elastic.Documentation.Navigation; +using Elastic.Documentation.Navigation.V2; using Elastic.Documentation.Site.FileProviders; namespace Elastic.Documentation.Site; @@ -68,6 +69,12 @@ public record GlobalLayoutViewModel public BuildType BuildType { get; init; } = BuildType.Isolated; + /// V2 section metadata for the secondary nav bar tabs. Null for V1 builds. + public IReadOnlyList? NavV2Sections { get; init; } + + /// The active section ID for highlighting the current tab in the secondary nav. + public string? ActiveSectionId { get; init; } + public bool RenderHamburgerIcon { get; init; } = true; /// White-label branding overrides. When non-null, all Elastic-specific chrome is suppressed. diff --git a/src/Elastic.Documentation.Svg/svgs/transition_bottom_in.svg b/src/Elastic.Documentation.Svg/svgs/transition_bottom_in.svg index 9e039dfa6d..27ea893e60 100644 --- a/src/Elastic.Documentation.Svg/svgs/transition_bottom_in.svg +++ b/src/Elastic.Documentation.Svg/svgs/transition_bottom_in.svg @@ -1 +1,6 @@ - + + + + + + diff --git a/src/Elastic.Documentation.Svg/svgs/transition_bottom_out.svg b/src/Elastic.Documentation.Svg/svgs/transition_bottom_out.svg index cc22af9376..943543d380 100644 --- a/src/Elastic.Documentation.Svg/svgs/transition_bottom_out.svg +++ b/src/Elastic.Documentation.Svg/svgs/transition_bottom_out.svg @@ -1 +1,6 @@ - + + + + + + diff --git a/src/Elastic.Markdown/Extensions/DetectionRules/DetectionRule.cs b/src/Elastic.Markdown/Extensions/DetectionRules/DetectionRule.cs index 7a66e4147a..bc3c7c4c1e 100644 --- a/src/Elastic.Markdown/Extensions/DetectionRules/DetectionRule.cs +++ b/src/Elastic.Markdown/Extensions/DetectionRules/DetectionRule.cs @@ -5,6 +5,7 @@ using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions; +using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -136,6 +137,36 @@ public static void InitializeVersionLock(IFileSystem fileSystem, IDirectoryInfo? } } + /// Fallback when upstream TOML uses constructs Tomlet cannot parse (e.g. newer table-array forms). + public static DetectionRule ForUnparsableSource(IFileInfo source, string parseErrorDetail) + { + var fullHex = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(source.FullName))); + var idSuffix = fullHex[..8].ToLowerInvariant(); + return new DetectionRule + { + Name = Path.GetFileNameWithoutExtension(source.Name), + Authors = [], + Note = parseErrorDetail, + Query = null, + Setup = null, + Tags = null, + Severity = "unknown", + RuleId = $"unparsed-{idSuffix}", + RiskScore = 0, + License = "Elastic-2.0", + Description = "This detection rule file could not be parsed as TOML during the documentation build.", + Type = "unknown", + Language = null, + Indices = null, + RunsEvery = null, + IndicesFromDateMath = null, + MaximumAlertsPerExecution = 100, + References = null, + Version = 1, + Threats = [] + }; + } + [SuppressMessage("Reliability", "CA2012:Use ValueTasks correctly")] public static DetectionRule From(IFileInfo source) { diff --git a/src/Elastic.Markdown/Extensions/DetectionRules/DetectionRuleFile.cs b/src/Elastic.Markdown/Extensions/DetectionRules/DetectionRuleFile.cs index 059fe728e7..3c28f0f8aa 100644 --- a/src/Elastic.Markdown/Extensions/DetectionRules/DetectionRuleFile.cs +++ b/src/Elastic.Markdown/Extensions/DetectionRules/DetectionRuleFile.cs @@ -97,7 +97,14 @@ BuildContext build { RuleSourceMarkdownPath = SourcePath(sourceFile, build); LinkReferenceRelativePath = Path.GetRelativePath(build.DocumentationSourceDirectory.FullName, RuleSourceMarkdownPath.FullName); - Rule = DetectionRule.From(sourceFile); + try + { + Rule = DetectionRule.From(sourceFile); + } + catch (Exception ex) + { + Rule = DetectionRule.ForUnparsableSource(sourceFile, ex.InnerException?.Message ?? ex.Message); + } } private static IFileInfo SourcePath(IFileInfo rulePath, BuildContext build) diff --git a/src/Elastic.Markdown/HtmlWriter.cs b/src/Elastic.Markdown/HtmlWriter.cs index 442f149616..64b7dd0afe 100644 --- a/src/Elastic.Markdown/HtmlWriter.cs +++ b/src/Elastic.Markdown/HtmlWriter.cs @@ -166,6 +166,8 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc NextDocument = next, Breadcrumbs = breadcrumbs, NavigationHtml = navigationHtmlRenderResult.Html, + NavV2Sections = navigationHtmlRenderResult.Sections, + ActiveSectionId = navigationHtmlRenderResult.ActiveSectionId, UrlPathPrefix = markdown.UrlPathPrefix, SiteRootPath = DocumentationSet.Context.SiteRootPath, AppliesTo = markdown.YamlFrontMatter?.AppliesTo, diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index 86333aba6a..158887d8bb 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -156,6 +156,28 @@ protected void ReadDocumentInstructions(MarkdownDocument document, Func block is HeadingBlock { Level: 1 })? .GetData("header") as string ?? Title; + // Fall back to any H1 nested in directive containers (e.g. {hero}) so layouts + // that wrap the page title in a directive still get title detection. + if (Title == RelativePath) + { + Title = document + .Descendants() + .FirstOrDefault(h => h.Level == 1)? + .GetData("header") as string ?? Title; + } + + // Fall back to a {hero} directive's :title: option -- hero pages express + // the title declaratively rather than via a body H1. + if (Title == RelativePath) + { + var heroTitle = document + .Descendants() + .FirstOrDefault()? + .Title; + if (!string.IsNullOrWhiteSpace(heroTitle)) + Title = heroTitle; + } + var yamlFrontMatter = ProcessYamlFrontMatter(document); YamlFrontMatter = yamlFrontMatter; if (yamlFrontMatter.NavigationTitle is not null) diff --git a/src/Elastic.Markdown/Layout/_LandingPage.cshtml b/src/Elastic.Markdown/Layout/_LandingPage.cshtml index 7dd11f233f..165992c42e 100644 --- a/src/Elastic.Markdown/Layout/_LandingPage.cshtml +++ b/src/Elastic.Markdown/Layout/_LandingPage.cshtml @@ -1,300 +1,632 @@ @inherits RazorSlice -
    -
    -
    -
    -

    Elastic Docs

    -

    Welcome to the technical documentation for Elastic Stack 9.0.0 - and later, and Elastic Cloud Serverless. Deploy, operate, and scale with step-by-step guides, API references, examples, and best practices.

    -

    Upgrading from an earlier version? Follow the upgrade guide to plan and complete your upgrade.

    + + +
    + + +
    +
    +
    +

    You're viewing docs for Elastic Stack 9.0+ (latest @Model.CurrentVersion)

    +

    Need docs for an earlier version?

    +
    +
    + 8.x docs (elastic.co/guide) → + @if (Model.AllVersionsUrl is not null) + { + All versions + } +
    +
    +
    + + +
    +

    Elastic documentation

    +

    Find guides, API references, and tutorials for the Elastic Stack.

    +
    + + +
    +
    + + +
    +
    + NEW + Elasticsearch @Model.CurrentVersion is out — check out what's new → +
    +
    + + + + + + + + +
    +
    +

    Browse by solution

    +
    +
    +
    +

    Search

    +

    Build search experiences powered by Elasticsearch — from full-text search to vector and hybrid retrieval.

    + +
    +
    +

    Observability

    +

    Logs, metrics, APM, and uptime monitoring in a unified platform.

    + +
    +
    +

    Security

    +

    SIEM, endpoint protection, and threat hunting for your organization.

    + +
    +
    +
    + + + + + + -
    -
    -
    -
    -

    Looking for more?

    -

    We'll show you how to solve toughest challenges with Search AI. - Join us at an Elastic{ON} event near you!

    -
    - - Register now - -
    -
    -
    -
    diff --git a/src/Elastic.Markdown/Layout/_TableOfContents.cshtml b/src/Elastic.Markdown/Layout/_TableOfContents.cshtml index 48eaf6a875..7694b6cfa3 100644 --- a/src/Elastic.Markdown/Layout/_TableOfContents.cshtml +++ b/src/Elastic.Markdown/Layout/_TableOfContents.cshtml @@ -107,9 +107,9 @@
      @foreach (var item in Model.PageTocItems) { -
    • +
    • @item.Heading diff --git a/src/Elastic.Markdown/MarkdownPageLayout.cs b/src/Elastic.Markdown/MarkdownPageLayout.cs index 6563b2a0fc..7f80d07134 100644 --- a/src/Elastic.Markdown/MarkdownPageLayout.cs +++ b/src/Elastic.Markdown/MarkdownPageLayout.cs @@ -11,5 +11,6 @@ public enum MarkdownPageLayout [EnumMember(Value = "landing-page")] LandingPage, [EnumMember(Value = "not-found")] NotFound, [EnumMember(Value = "archive")] Archive, - [EnumMember(Value = "full-search")] FullSearch + [EnumMember(Value = "full-search")] FullSearch, + [EnumMember(Value = "hub")] Hub } diff --git a/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs b/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs index a6a3dd4a6d..14708ddaf0 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicabilityRenderer.cs @@ -164,8 +164,10 @@ private static PopoverData BuildPopoverData( var showVersionNote = productInfo is { IncludeVersionNote: true } && versioningSystem.IsVersioned(); + var description = InterpolateVersionPlaceholders(productInfo?.Description, versioningSystem); + return new PopoverData( - ProductDescription: productInfo?.Description, + ProductDescription: description, AvailabilityItems: orderedApplicabilities.Select(applicability => BuildAvailabilityItem(applicability, versioningSystem, productName, applicabilities.Count)).OfType().ToArray(), AdditionalInfo: productInfo?.AdditionalAvailabilityInfo, ShowVersionNote: showVersionNote, @@ -173,6 +175,22 @@ private static PopoverData BuildPopoverData( ); } + /// + /// Substitutes {base}, {current}, and {base-major} placeholders in + /// product descriptions with values from the versioning system, so tooltips can surface + /// the actual version range without hardcoding it in ProductDescriptions. + /// + private static string? InterpolateVersionPlaceholders(string? template, VersioningSystem versioningSystem) + { + if (string.IsNullOrEmpty(template)) + return template; + + return template + .Replace("{base}", versioningSystem.Base.ToString()) + .Replace("{current}", versioningSystem.Current.ToString()) + .Replace("{base-major}", versioningSystem.Base.Major.ToString()); + } + /// /// Builds an availability item for an applicability entry. /// Returns null if the item should not be added to the availability list. diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs index 82afa2ce5f..1f0992f4a3 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs @@ -16,7 +16,13 @@ public enum ApplicabilityBadgePlacement /// Elastic Stack (and generic product) availability next to the setting name. StackRow, /// Deployment, serverless, and product-specific agents on a "Supported on" line. - SupportedOnRow + SupportedOnRow, + /// Stack versions row of the page-top metadata box: applies_to.stack + applies_to.serverless when no deployment is specified. + StackVersionsRow, + /// Deployments row of the page-top metadata box: applies_to.deployment + applies_to.serverless when a deployment is specified. + DeploymentsRow, + /// Other products row of the page-top metadata box: applies_to.product and per-product applicabilities (agents, EDOT, etc.). + OtherProductsRow } public class ApplicableToViewModel @@ -79,10 +85,30 @@ public IReadOnlyCollection GetApplicabilityItems() { ApplicabilityBadgePlacement.StackRow => CollectStackRowRaw(), ApplicabilityBadgePlacement.SupportedOnRow => CollectSupportedOnRaw(), + ApplicabilityBadgePlacement.StackVersionsRow => CollectStackVersionsRowRaw(), + ApplicabilityBadgePlacement.DeploymentsRow => CollectDeploymentsRowRaw(), + ApplicabilityBadgePlacement.OtherProductsRow => CollectOtherProductsRowRaw(), _ => CollectCombinedRaw() }; - return RenderGroupedItems(rawItems).ToArray(); + var rendered = RenderGroupedItems(rawItems); + + // In the metadata-box rows, surface Serverless first, then other available + // items, then unavailable items at the end. The CSS suppresses the "or" + // separator before unavailable items so they read as a distinct group. + if (BadgePlacement is ApplicabilityBadgePlacement.StackVersionsRow + or ApplicabilityBadgePlacement.DeploymentsRow + or ApplicabilityBadgePlacement.OtherProductsRow) + { + rendered = rendered + .Select((item, index) => (item, index)) + .OrderBy(x => x.item.RenderData.LifecycleClass == "unavailable" ? 1 : 0) + .ThenBy(x => x.item.Key.StartsWith("Serverless", StringComparison.Ordinal) ? 0 : 1) + .ThenBy(x => x.index) + .Select(x => x.item); + } + + return rendered.ToArray(); } private List CollectCombinedRaw() @@ -154,6 +180,55 @@ AppliesTo.Serverless is null && .ToList(); } + private List CollectStackVersionsRowRaw() + { + var rawItems = new List(); + + if (AppliesTo.Stack is not null) + rawItems.AddRange(CollectFromCollection(AppliesTo.Stack, ApplicabilityMappings.Stack)); + + if (AppliesTo.Deployment is null && AppliesTo.Serverless is not null) + { + rawItems.AddRange(AppliesTo.Serverless.AllProjects is not null + ? CollectFromCollection(AppliesTo.Serverless.AllProjects, ApplicabilityMappings.Serverless) + : CollectFromMappings(AppliesTo.Serverless, ServerlessMappings)); + } + + return rawItems; + } + + private List CollectDeploymentsRowRaw() + { + var rawItems = new List(); + + if (AppliesTo.Deployment is not null) + { + rawItems.AddRange(CollectFromMappings(AppliesTo.Deployment, DeploymentMappings)); + + if (AppliesTo.Serverless is not null) + { + rawItems.AddRange(AppliesTo.Serverless.AllProjects is not null + ? CollectFromCollection(AppliesTo.Serverless.AllProjects, ApplicabilityMappings.Serverless) + : CollectFromMappings(AppliesTo.Serverless, ServerlessMappings)); + } + } + + return rawItems; + } + + private List CollectOtherProductsRowRaw() + { + var rawItems = new List(); + + if (AppliesTo.ProductApplicability is not null) + rawItems.AddRange(CollectFromMappings(AppliesTo.ProductApplicability, ProductMappings)); + + if (AppliesTo.Product is not null) + rawItems.AddRange(CollectFromCollection(AppliesTo.Product, ApplicabilityMappings.Product)); + + return rawItems; + } + private static bool IsGenericGa(AppliesCollection collection) { if (collection.Count != 1) diff --git a/src/Elastic.Markdown/Myst/Components/ProductDescriptions.cs b/src/Elastic.Markdown/Myst/Components/ProductDescriptions.cs index bee9e8a7c8..612fd1af87 100644 --- a/src/Elastic.Markdown/Myst/Components/ProductDescriptions.cs +++ b/src/Elastic.Markdown/Myst/Components/ProductDescriptions.cs @@ -34,33 +34,34 @@ bool IncludeVersionNote private static readonly Dictionary Descriptions = new() { - // Stack + // Stack -- {base} and {current} are interpolated by ApplicabilityRenderer + // from the Stack VersioningSystem so the tooltip surfaces the actual range. [VersioningSystemId.Stack] = new ProductInfo( - Description: "The Elastic Stack includes Elastic's core products such as Elasticsearch, Kibana, Logstash, and Beats.", + Description: "This documentation applies to Elastic Stack {base} to {current}. To know if this functionality exists in versions prior to v{base-major}, refer to the corresponding version of the documentation.", AdditionalAvailabilityInfo: "Unless stated otherwise on the page, this functionality is available when your Elastic Stack is deployed on Elastic Cloud Hosted, Elastic Cloud Enterprise, Elastic Cloud on Kubernetes, and self-managed environments.", IncludeVersionNote: true ), // Serverless [VersioningSystemId.Serverless] = new ProductInfo( - Description: "Elastic Cloud Serverless projects are autoscaled environments, fully managed by Elastic and available on Elastic Cloud.", + Description: "Elastic Cloud Serverless projects are autoscaled environments, fully managed by Elastic and available on Elastic Cloud. Their UI and capabilities can differ from the traditionally versioned Elastic Stack.", AdditionalAvailabilityInfo: "Serverless interfaces and procedures might differ from classic Elastic Stack deployments.", IncludeVersionNote: false ), // Serverless Project Types [VersioningSystemId.ElasticsearchProject] = new ProductInfo( - Description: "Elastic Cloud Serverless projects are autoscaled environments, fully managed by Elastic and available on Elastic Cloud.", + Description: "Elastic Cloud Serverless projects are autoscaled environments, fully managed by Elastic and available on Elastic Cloud. Their UI and capabilities can differ from the traditionally versioned Elastic Stack.", AdditionalAvailabilityInfo: null, IncludeVersionNote: false ), [VersioningSystemId.ObservabilityProject] = new ProductInfo( - Description: "Elastic Cloud Serverless projects are autoscaled environments, fully managed by Elastic and available on Elastic Cloud.", + Description: "Elastic Cloud Serverless projects are autoscaled environments, fully managed by Elastic and available on Elastic Cloud. Their UI and capabilities can differ from the traditionally versioned Elastic Stack.", AdditionalAvailabilityInfo: null, IncludeVersionNote: false ), [VersioningSystemId.SecurityProject] = new ProductInfo( - Description: "Elastic Cloud Serverless projects are autoscaled environments, fully managed by Elastic and available on Elastic Cloud.", + Description: "Elastic Cloud Serverless projects are autoscaled environments, fully managed by Elastic and available on Elastic Cloud. Their UI and capabilities can differ from the traditionally versioned Elastic Stack.", AdditionalAvailabilityInfo: null, IncludeVersionNote: false ), diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs index 770565af64..f17e380490 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs @@ -9,6 +9,7 @@ using Elastic.Markdown.Myst.Directives.Button; using Elastic.Markdown.Myst.Directives.Changelog; using Elastic.Markdown.Myst.Directives.CsvInclude; +using Elastic.Markdown.Myst.Directives.Hub; using Elastic.Markdown.Myst.Directives.Image; using Elastic.Markdown.Myst.Directives.Include; using Elastic.Markdown.Myst.Directives.Math; @@ -137,6 +138,24 @@ protected override DirectiveBlock CreateFencedBlock(BlockProcessor processor) if (info.IndexOf("{agent-skill}") > 0) return new AgentSkillBlock(this, context); + if (info.IndexOf("{hero}") > 0) + return new HeroBlock(this, context); + + if (info.IndexOf("{card-group}") > 0) + return new CardGroupBlock(this, context); + + if (info.IndexOf("{link-card}") > 0) + return new LinkCardBlock(this, context); + + if (info.IndexOf("{whats-new}") > 0) + return new WhatsNewBlock(this, context); + + if (info.IndexOf("{intro}") > 0) + return new IntroBlock(this, context); + + if (info.IndexOf("{on-this-page}") > 0) + return new OnThisPageBlock(this, context); + foreach (var admonition in Admonitions) { if (info.IndexOf(admonition) > 0) diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index 838713abbf..8934da227a 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -15,6 +15,7 @@ using Elastic.Markdown.Myst.Directives.Changelog; using Elastic.Markdown.Myst.Directives.CsvInclude; using Elastic.Markdown.Myst.Directives.Dropdown; +using Elastic.Markdown.Myst.Directives.Hub; using Elastic.Markdown.Myst.Directives.Image; using Elastic.Markdown.Myst.Directives.Include; using Elastic.Markdown.Myst.Directives.Math; @@ -125,6 +126,24 @@ protected override void Write(HtmlRenderer renderer, DirectiveBlock directiveBlo case AgentSkillBlock agentSkillBlock: WriteAgentSkill(renderer, agentSkillBlock); return; + case HeroBlock heroBlock: + WriteHero(renderer, heroBlock); + return; + case CardGroupBlock cardGroupBlock: + WriteCardGroup(renderer, cardGroupBlock); + return; + case LinkCardBlock linkCardBlock: + WriteLinkCard(renderer, linkCardBlock); + return; + case WhatsNewBlock whatsNewBlock: + WriteWhatsNew(renderer, whatsNewBlock); + return; + case IntroBlock introBlock: + WriteIntro(renderer, introBlock); + return; + case OnThisPageBlock onThisPageBlock: + WriteOnThisPage(renderer, onThisPageBlock); + return; default: // if (!string.IsNullOrEmpty(directiveBlock.Info) && !directiveBlock.Info.StartsWith('{')) // WriteCode(renderer, directiveBlock); @@ -347,6 +366,93 @@ private static void WriteTabSet(HtmlRenderer renderer, TabSetBlock block) RenderRazorSlice(slice, renderer); } + private static void WriteHero(HtmlRenderer renderer, HeroBlock block) + { + var releasesHtml = string.IsNullOrWhiteSpace(block.Releases) + ? null + : RenderInlineMarkdown(block.Releases); + + var descriptionHtml = string.IsNullOrWhiteSpace(block.Description) + ? null + : RenderInlineMarkdown(block.Description); + + var slice = HeroView.Create(new HeroViewModel + { + DirectiveBlock = block, + IconKey = block.Icon, + IconSvg = block.IconSvg, + Title = block.Title, + DescriptionHtml = descriptionHtml, + Version = block.Version, + ShowSearch = block.ShowSearch, + QuickLinks = block.QuickLinks, + OtherVersions = block.OtherVersions, + ReleasesHtml = releasesHtml + }); + RenderRazorSlice(slice, renderer); + } + + private static void WriteCardGroup(HtmlRenderer renderer, CardGroupBlock block) + { + var slice = CardGroupView.Create(new CardGroupViewModel + { + DirectiveBlock = block, + Title = block.Title, + Intro = block.Intro, + Anchor = block.Anchor, + Variant = block.Variant + }); + RenderRazorSlice(slice, renderer); + } + + private static void WriteLinkCard(HtmlRenderer renderer, LinkCardBlock block) + { + var slice = LinkCardView.Create(new LinkCardViewModel + { + DirectiveBlock = block, + Data = block.Data, + IconSvg = ProductIcons.Get(block.Data.Icon) + }); + RenderRazorSlice(slice, renderer); + } + + private static void WriteWhatsNew(HtmlRenderer renderer, WhatsNewBlock block) + { + var slice = WhatsNewView.Create(new WhatsNewViewModel + { + DirectiveBlock = block, + Data = block.Data + }); + RenderRazorSlice(slice, renderer); + } + + private static void WriteIntro(HtmlRenderer renderer, IntroBlock block) + { + var slice = IntroView.Create(new IntroViewModel { DirectiveBlock = block }); + RenderRazorSlice(slice, renderer); + } + + private static void WriteOnThisPage(HtmlRenderer renderer, OnThisPageBlock block) + { + var slice = OnThisPageView.Create(new OnThisPageViewModel + { + DirectiveBlock = block, + Items = block.CollectItems() + }); + RenderRazorSlice(slice, renderer); + } + + private static string RenderInlineMarkdown(string source) + { + var html = Markdig.Markdown.ToHtml(source).Trim(); + // Strip a single wrapping

      ...

      so the result can drop into a span/p directly. + const string open = "

      "; + const string close = "

      "; + if (html.StartsWith(open, StringComparison.Ordinal) && html.EndsWith(close, StringComparison.Ordinal)) + html = html[open.Length..^close.Length]; + return html; + } + private static void WriteTabItem(HtmlRenderer renderer, TabItemBlock block) { var slice = TabItemView.Create(new TabItemViewModel diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/CardGroupBlock.cs b/src/Elastic.Markdown/Myst/Directives/Hub/CardGroupBlock.cs new file mode 100644 index 0000000000..ed6a11d43a --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/CardGroupBlock.cs @@ -0,0 +1,46 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 + +namespace Elastic.Markdown.Myst.Directives.Hub; + +/// +/// Container directive that renders a titled section housing a grid of +/// children. Generic and reusable wherever a +/// linked-card grid is appropriate. +/// +/// +/// +/// ::::{card-group} +/// :title: Install and deploy +/// :intro: Set up Elasticsearch on your platform of choice. +/// :id: install +/// +/// :::{link-card} Self-managed +/// :link: /deploy-manage/deploy/self-managed +/// Run on your own infrastructure. +/// ::: +/// :::: +/// +/// +public class CardGroupBlock(DirectiveBlockParser parser, ParserContext context) + : DirectiveBlock(parser, context) +{ + public override string Directive => "card-group"; + + public string? Title { get; private set; } + public string? Intro { get; private set; } + public string? Anchor { get; private set; } + public string? Variant { get; private set; } + + public override void FinalizeAndValidate(ParserContext context) + { + Title = Prop("title"); + Intro = Prop("intro"); + Anchor = Prop("id"); + Variant = Prop("variant"); + } + + public override IEnumerable GeneratedAnchors => + string.IsNullOrWhiteSpace(Anchor) ? [] : [Anchor]; +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/CardGroupView.cshtml b/src/Elastic.Markdown/Myst/Directives/Hub/CardGroupView.cshtml new file mode 100644 index 0000000000..28f9d6cc86 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/CardGroupView.cshtml @@ -0,0 +1,15 @@ +@inherits RazorSlice + +@if (!string.IsNullOrWhiteSpace(Model.Title)) +{ +
      +

      @Model.Title

      + @if (!string.IsNullOrWhiteSpace(Model.Intro)) + { +

      @Model.Intro

      + } +
      +} +
        + @Model.RenderBlock() +
      diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/CardGroupViewModel.cs b/src/Elastic.Markdown/Myst/Directives/Hub/CardGroupViewModel.cs new file mode 100644 index 0000000000..d75d8716ac --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/CardGroupViewModel.cs @@ -0,0 +1,13 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 + +namespace Elastic.Markdown.Myst.Directives.Hub; + +public class CardGroupViewModel : DirectiveViewModel +{ + public required string? Title { get; init; } + public required string? Intro { get; init; } + public required string? Anchor { get; init; } + public required string? Variant { get; init; } +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/HeroBlock.cs b/src/Elastic.Markdown/Myst/Directives/Hub/HeroBlock.cs new file mode 100644 index 0000000000..8787ad312f --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/HeroBlock.cs @@ -0,0 +1,99 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Markdown.Diagnostics; + +namespace Elastic.Markdown.Myst.Directives.Hub; + +/// +/// Renders a full-bleed page hero with product icon, title, description, +/// search box, version chip, quick-link pills, and an optional release-status +/// line. All content is supplied via options -- the body is unused. +/// +/// +/// +/// :::{hero} +/// :icon: kibana +/// :title: Kibana +/// :description: The UI for the Elasticsearch platform. +/// :version: v9 / Serverless (current) +/// :quick-links: Install=/install,Tutorial=/tutorial +/// :releases: Latest: [Stack 9.4.1](/rn) (Mar 28, 2026) +/// ::: +/// +/// +public class HeroBlock(DirectiveBlockParser parser, ParserContext context) + : DirectiveBlock(parser, context) +{ + public override string Directive => "hero"; + + public string? Icon { get; private set; } + public string? IconSvg { get; private set; } + public string? Title { get; private set; } + public string? Description { get; private set; } + public string? Version { get; private set; } + public bool ShowSearch { get; private set; } + public IReadOnlyList QuickLinks { get; private set; } = []; + public IReadOnlyList OtherVersions { get; private set; } = []; + public string? Releases { get; private set; } + + public override void FinalizeAndValidate(ParserContext context) + { + Icon = Prop("icon"); + IconSvg = ProductIcons.Get(Icon); + Title = Prop("title"); + Description = Prop("description"); + Version = Prop("version"); + // search defaults to true; explicit ":search: false" hides it + ShowSearch = TryPropBool("search") ?? true; + QuickLinks = ParsePairs(Prop("quick-links"), allowEmptyUrl: false) + .Select(p => new HeroQuickLink(p.Label, p.Url!)).ToList(); + OtherVersions = ParsePairs(Prop("versions"), allowEmptyUrl: true) + .Select(p => new HeroVersion(p.Label, p.Url)).ToList(); + Releases = Prop("releases"); + + if (string.IsNullOrWhiteSpace(Title)) + this.EmitError("{hero} requires a `:title:` option."); + } + + private static IReadOnlyList<(string Label, string? Url)> ParsePairs(string? raw, bool allowEmptyUrl) + { + if (string.IsNullOrWhiteSpace(raw)) + return []; + + var entries = new List<(string, string?)>(); + foreach (var part in raw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) + { + var separator = part.IndexOf('='); + string label; + string? url; + if (separator < 0) + { + if (!allowEmptyUrl) + continue; + label = part.Trim(); + url = null; + } + else + { + label = part[..separator].Trim(); + url = part[(separator + 1)..].Trim(); + if (string.IsNullOrEmpty(url)) + { + if (!allowEmptyUrl) + continue; + url = null; + } + } + if (label.Length > 0) + entries.Add((label, url)); + } + + return entries; + } +} + +public readonly record struct HeroQuickLink(string Label, string Url); + +public readonly record struct HeroVersion(string Label, string? Url); diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/HeroView.cshtml b/src/Elastic.Markdown/Myst/Directives/Hub/HeroView.cshtml new file mode 100644 index 0000000000..d25284d072 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/HeroView.cshtml @@ -0,0 +1,101 @@ +@inherits RazorSlice + +
      +
      +
      + @if (!string.IsNullOrWhiteSpace(Model.IconSvg)) + { + + @(new HtmlString(Model.IconSvg)) + + } + else if (!string.IsNullOrWhiteSpace(Model.IconKey)) + { + + } +
      +

      @Model.Title

      + @if (!string.IsNullOrWhiteSpace(Model.DescriptionHtml)) + { +

      @(new HtmlString(Model.DescriptionHtml))

      + } +
      +
      + + @if (Model.ShowSearch) + { + var placeholder = string.IsNullOrWhiteSpace(Model.IconKey) + ? "Search docs..." + : $"Search {char.ToUpperInvariant(Model.IconKey![0])}{Model.IconKey.AsSpan(1).ToString()} docs..."; + + } + +
      + @if (!string.IsNullOrWhiteSpace(Model.Version)) + { + if (Model.OtherVersions.Count > 0) + { +
      + + + @Model.Version + + + +
      + } + else + { + + + @Model.Version + + } + } + @if (Model.QuickLinks.Count > 0) + { +
        + @foreach (var link in Model.QuickLinks) + { +
      • @link.Label
      • + } +
      + } +
      + + @if (!string.IsNullOrWhiteSpace(Model.ReleasesHtml)) + { +

      @(new HtmlString(Model.ReleasesHtml))

      + } +
      +
      diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/HeroViewModel.cs b/src/Elastic.Markdown/Myst/Directives/Hub/HeroViewModel.cs new file mode 100644 index 0000000000..7c971bdd02 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/HeroViewModel.cs @@ -0,0 +1,18 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 + +namespace Elastic.Markdown.Myst.Directives.Hub; + +public class HeroViewModel : DirectiveViewModel +{ + public required string? IconKey { get; init; } + public required string? IconSvg { get; init; } + public required string? Title { get; init; } + public required string? DescriptionHtml { get; init; } + public required string? Version { get; init; } + public required bool ShowSearch { get; init; } + public required IReadOnlyList QuickLinks { get; init; } + public required IReadOnlyList OtherVersions { get; init; } + public required string? ReleasesHtml { get; init; } +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/HubYamlBody.cs b/src/Elastic.Markdown/Myst/Directives/Hub/HubYamlBody.cs new file mode 100644 index 0000000000..fe885e3ce0 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/HubYamlBody.cs @@ -0,0 +1,96 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 Markdig.Syntax; + +namespace Elastic.Markdown.Myst.Directives.Hub; + +/// +/// Helpers for directives that read their body as raw YAML directly from the +/// source file. The directive's children remain markdown blocks (so renderers +/// that don't recognize the directive still produce something), but the +/// canonical structured data comes from the YAML. +/// +internal static class HubYamlBody +{ + /// + /// Returns the raw text between the opening and closing fences of a directive + /// block, or null when no fenced body could be located. + /// + public static string? Extract(IBlockExtension block, IFileSystemFileReader reader) + { + if (block is not Block markdig) + return null; + + string source; + try + { + source = reader.ReadAllText(block.CurrentFile.FullName); + } + catch + { + return null; + } + + var lines = source.Split('\n'); + var openingLine = markdig.Line; + if (openingLine < 0 || openingLine >= lines.Length) + return null; + + var fence = ExtractFenceMarker(lines[openingLine]); + if (fence is null) + return null; + + var closingLine = -1; + for (var i = openingLine + 1; i < lines.Length; i++) + { + var trimmed = lines[i].TrimStart(); + if (trimmed.StartsWith(fence, StringComparison.Ordinal) && IsClosingFence(trimmed, fence)) + { + closingLine = i; + break; + } + } + if (closingLine < 0) + return null; + + var body = string.Join('\n', lines, openingLine + 1, closingLine - openingLine - 1); + return string.IsNullOrWhiteSpace(body) ? null : body; + } + + private static string? ExtractFenceMarker(string openingLine) + { + var trimmed = openingLine.TrimStart(); + var count = 0; + while (count < trimmed.Length && trimmed[count] == ':') + count++; + return count >= 3 ? new string(':', count) : null; + } + + private static bool IsClosingFence(string trimmed, string fence) + { + if (!trimmed.StartsWith(fence, StringComparison.Ordinal)) + return false; + for (var i = fence.Length; i < trimmed.Length; i++) + { + if (!char.IsWhiteSpace(trimmed[i])) + return false; + } + return true; + } +} + +/// +/// Minimal abstraction over file reading so HubYamlBody can be tested without a +/// full BuildContext. +/// +public interface IFileSystemFileReader +{ + string ReadAllText(string path); +} + +internal sealed class BuildContextFileReader(System.IO.Abstractions.IFileSystem fileSystem) : IFileSystemFileReader +{ + public string ReadAllText(string path) => fileSystem.File.ReadAllText(path); +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/IntroBlock.cs b/src/Elastic.Markdown/Myst/Directives/Hub/IntroBlock.cs new file mode 100644 index 0000000000..e6f350dfd6 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/IntroBlock.cs @@ -0,0 +1,26 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 + +namespace Elastic.Markdown.Myst.Directives.Hub; + +/// +/// A small "getting started" callout panel shown between the hero and the first +/// section. The body is rendered as inline markdown so authors can use bold, +/// links, and emphasis. Visually a white rounded panel with a teal accent bar. +/// +/// +/// +/// :::{intro} +/// **New to Kibana?** [Learn data exploration and visualization](/learn), a hands-on +/// walk-through of Discover, ES|QL, visualizations, and dashboards. +/// ::: +/// +/// +public class IntroBlock(DirectiveBlockParser parser, ParserContext context) + : DirectiveBlock(parser, context) +{ + public override string Directive => "intro"; + + public override void FinalizeAndValidate(ParserContext context) { } +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/IntroView.cshtml b/src/Elastic.Markdown/Myst/Directives/Hub/IntroView.cshtml new file mode 100644 index 0000000000..8f47ae9ee9 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/IntroView.cshtml @@ -0,0 +1,5 @@ +@inherits RazorSlice + + diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/IntroViewModel.cs b/src/Elastic.Markdown/Myst/Directives/Hub/IntroViewModel.cs new file mode 100644 index 0000000000..f639218cea --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/IntroViewModel.cs @@ -0,0 +1,7 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 + +namespace Elastic.Markdown.Myst.Directives.Hub; + +public class IntroViewModel : DirectiveViewModel; diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/LinkCardBlock.cs b/src/Elastic.Markdown/Myst/Directives/Hub/LinkCardBlock.cs new file mode 100644 index 0000000000..a116fd6e81 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/LinkCardBlock.cs @@ -0,0 +1,115 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Markdown.Diagnostics; +using YamlDotNet.Core; +using YamlDotNet.Serialization; + +namespace Elastic.Markdown.Myst.Directives.Hub; + +/// +/// A rich card with title, link, description, primary-link list, and an optional +/// aside (e.g. "Panel types: A · B · C"). The card schema is YAML-formatted in the +/// directive body for predictable structure. +/// +/// +/// +/// :::{link-card} +/// title: Discover +/// link: /discover/ +/// description: Browse documents, filter, and query your indices in real time. +/// links: +/// - label: Get started with Discover +/// url: /discover/get-started +/// - label: Use ES|QL in Kibana +/// url: /esql +/// aside: +/// label: Panel types +/// links: +/// - label: Visualizations +/// url: /viz +/// - label: Maps +/// url: /maps +/// ::: +/// +/// +public class LinkCardBlock(DirectiveBlockParser parser, ParserContext context) + : DirectiveBlock(parser, context), IBlockTitle +{ + public override string Directive => "link-card"; + + public LinkCardData Data { get; private set; } = LinkCardData.Empty; + + public string Title => Data.Title ?? string.Empty; + + public override void FinalizeAndValidate(ParserContext context) + { + var yaml = HubYamlBody.Extract(this, new BuildContextFileReader(Build.ReadFileSystem)); + if (yaml is null) + { + this.EmitError("{link-card} requires a YAML body. See the link-card directive docs."); + return; + } + + try + { + Data = YamlSerialization.Deserialize(yaml, Build.ProductsConfiguration) ?? LinkCardData.Empty; + } + catch (YamlException ex) + { + this.EmitError($"{{link-card}} YAML parse error: {ex.Message}"); + return; + } + + if (string.IsNullOrWhiteSpace(Data.Title)) + this.EmitError("{link-card} requires a `title` field in its YAML body."); + } +} + +[YamlSerializable] +public record LinkCardData +{ + [YamlMember(Alias = "title")] + public string? Title { get; set; } + + [YamlMember(Alias = "link")] + public string? Link { get; set; } + + [YamlMember(Alias = "description")] + public string? Description { get; set; } + + [YamlMember(Alias = "icon")] + public string? Icon { get; set; } + + [YamlMember(Alias = "variant")] + public string? Variant { get; set; } + + [YamlMember(Alias = "links")] + public LinkCardLink[] Links { get; set; } = []; + + [YamlMember(Alias = "aside")] + public LinkCardAside? Aside { get; set; } + + public static LinkCardData Empty { get; } = new(); +} + +[YamlSerializable] +public record LinkCardLink +{ + [YamlMember(Alias = "label")] + public string? Label { get; set; } + + [YamlMember(Alias = "url")] + public string? Url { get; set; } +} + +[YamlSerializable] +public record LinkCardAside +{ + [YamlMember(Alias = "label")] + public string? Label { get; set; } + + [YamlMember(Alias = "links")] + public LinkCardLink[] Links { get; set; } = []; +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/LinkCardView.cshtml b/src/Elastic.Markdown/Myst/Directives/Hub/LinkCardView.cshtml new file mode 100644 index 0000000000..2017da0d05 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/LinkCardView.cshtml @@ -0,0 +1,67 @@ +@inherits RazorSlice + +@{ + var d = Model.Data; + var classes = "hub-card"; + if (!string.IsNullOrWhiteSpace(d.Variant)) + classes += " hub-card-sol hub-card-sol-" + d.Variant; +} + +
    • +
      + @if (!string.IsNullOrWhiteSpace(Model.IconSvg)) + { + @(new HtmlString(Model.IconSvg)) + } +

      + @if (!string.IsNullOrWhiteSpace(d.Link)) + { + @d.Title + } + else + { + @d.Title + } +

      +
      + + @if (!string.IsNullOrWhiteSpace(d.Description)) + { +

      @d.Description

      + } + + @if (d.Links.Length > 0) + { + + } + + @if (d.Aside is { } aside && aside.Links.Length > 0) + { +
      + @if (!string.IsNullOrWhiteSpace(aside.Label)) + { +
      @aside.Label
      + } + +
      + } +
    • diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/LinkCardViewModel.cs b/src/Elastic.Markdown/Myst/Directives/Hub/LinkCardViewModel.cs new file mode 100644 index 0000000000..ff75e4821f --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/LinkCardViewModel.cs @@ -0,0 +1,11 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 + +namespace Elastic.Markdown.Myst.Directives.Hub; + +public class LinkCardViewModel : DirectiveViewModel +{ + public required LinkCardData Data { get; init; } + public required string? IconSvg { get; init; } +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/OnThisPageBlock.cs b/src/Elastic.Markdown/Myst/Directives/Hub/OnThisPageBlock.cs new file mode 100644 index 0000000000..11f6f9a382 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/OnThisPageBlock.cs @@ -0,0 +1,54 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 Markdig.Syntax; + +namespace Elastic.Markdown.Myst.Directives.Hub; + +/// +/// Inline table-of-contents chip listing every {card-group} and {whats-new} +/// section on the page that has a non-empty id and title. Auto-generated; no +/// options or body required. +/// +/// +/// +/// :::{on-this-page} +/// ::: +/// +/// +public class OnThisPageBlock(DirectiveBlockParser parser, ParserContext context) + : DirectiveBlock(parser, context) +{ + public override string Directive => "on-this-page"; + + public override void FinalizeAndValidate(ParserContext context) { } + + /// + /// Resolved at render time, since later sibling card-groups may not be + /// validated yet at parse time. + /// + public IReadOnlyList CollectItems() + { + var current = (ContainerBlock)this; + while (current.Parent is not null) + current = current.Parent; + + var items = new List(); + foreach (var descendant in current.Descendants()) + { + switch (descendant) + { + case CardGroupBlock g when !string.IsNullOrWhiteSpace(g.Anchor) && !string.IsNullOrWhiteSpace(g.Title): + items.Add(new OnThisPageItem(g.Title, g.Anchor)); + break; + case WhatsNewBlock w when !string.IsNullOrWhiteSpace(w.Data.Id) && !string.IsNullOrWhiteSpace(w.Data.Title): + items.Add(new OnThisPageItem(w.Data.Title, w.Data.Id)); + break; + } + } + return items; + } +} + +public readonly record struct OnThisPageItem(string Title, string Anchor); diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/OnThisPageView.cshtml b/src/Elastic.Markdown/Myst/Directives/Hub/OnThisPageView.cshtml new file mode 100644 index 0000000000..69b9231df0 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/OnThisPageView.cshtml @@ -0,0 +1,17 @@ +@inherits RazorSlice + +@if (Model.Items.Count > 0) +{ + +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/OnThisPageViewModel.cs b/src/Elastic.Markdown/Myst/Directives/Hub/OnThisPageViewModel.cs new file mode 100644 index 0000000000..e4f190c7a9 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/OnThisPageViewModel.cs @@ -0,0 +1,10 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 + +namespace Elastic.Markdown.Myst.Directives.Hub; + +public class OnThisPageViewModel : DirectiveViewModel +{ + public required IReadOnlyList Items { get; init; } +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/ProductIcons.cs b/src/Elastic.Markdown/Myst/Directives/Hub/ProductIcons.cs new file mode 100644 index 0000000000..3cd39b84ec --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/ProductIcons.cs @@ -0,0 +1,68 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 System.Collections.Frozen; + +namespace Elastic.Markdown.Myst.Directives.Hub; + +/// +/// Looks up an inline SVG by product key. Used by the {hero} directive (hero-icon +/// chip) and {link-card} (solution-card icon). Keys are kept lowercase and match +/// the product ids in products.yml wherever possible. +/// +public static class ProductIcons +{ + private static readonly FrozenDictionary Icons = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["elasticsearch"] = """ + + """, + ["kibana"] = """ + + """, + ["observability"] = """ + + """, + ["security"] = """ + + """ + }.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Returns the inline SVG markup for , or null when no icon + /// is registered for that product. + /// + public static string? Get(string? key) + { + if (string.IsNullOrWhiteSpace(key)) + return null; + return Icons.TryGetValue(key, out var svg) ? svg : null; + } + + /// + /// Initials fallback: the first character of the key, uppercased. Returns a + /// single-letter string, suitable for the standard hero-icon chip when no + /// SVG is registered. + /// + public static string Initials(string? key) => + string.IsNullOrWhiteSpace(key) ? "?" : char.ToUpperInvariant(key[0]).ToString(); +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/WhatsNewBlock.cs b/src/Elastic.Markdown/Myst/Directives/Hub/WhatsNewBlock.cs new file mode 100644 index 0000000000..f61250d815 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/WhatsNewBlock.cs @@ -0,0 +1,142 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 System.Collections.Concurrent; +using Elastic.Markdown.Diagnostics; +using YamlDotNet.Core; +using YamlDotNet.Serialization; + +namespace Elastic.Markdown.Myst.Directives.Hub; + +/// +/// Renders the "What's new" panel for a product. Two usage shapes: +/// +/// Centralized lookup (preferred): +/// +/// :::{whats-new} +/// :product: kibana +/// ::: +/// +/// The directive looks the product key up in config/whats-new.yml +/// and renders the data declared there. Authors edit one file; any page can +/// surface the panel. +/// +/// Inline override: if no :product: option is provided, +/// the directive expects a YAML body declaring title, items, +/// etc. directly. Useful for one-offs that don't belong in the central +/// feed. +/// +public class WhatsNewBlock(DirectiveBlockParser parser, ParserContext context) + : DirectiveBlock(parser, context) +{ + private static readonly ConcurrentDictionary CentralConfigCache = new(); + + public override string Directive => "whats-new"; + + public WhatsNewData Data { get; private set; } = WhatsNewData.Empty; + + public override void FinalizeAndValidate(ParserContext context) + { + var product = Prop("product"); + + if (!string.IsNullOrWhiteSpace(product)) + { + var resolved = LoadFromCentralConfig(product); + if (resolved is null) + { + this.EmitError($"{{whats-new}} :product: '{product}' was not found in config/whats-new.yml."); + return; + } + Data = resolved; + return; + } + + var yaml = HubYamlBody.Extract(this, new BuildContextFileReader(Build.ReadFileSystem)); + if (yaml is null) + { + this.EmitError("{whats-new} requires either a `:product:` option or a YAML body."); + return; + } + + try + { + Data = YamlSerialization.Deserialize(yaml, Build.ProductsConfiguration) ?? WhatsNewData.Empty; + } + catch (YamlException ex) + { + this.EmitError($"{{whats-new}} YAML parse error: {ex.Message}"); + } + } + + private WhatsNewData? LoadFromCentralConfig(string productKey) + { + var configFile = Build.ConfigurationFileProvider.WhatsNewFile; + if (configFile is null || !configFile.Exists) + return null; + + var config = CentralConfigCache.GetOrAdd(configFile.FullName, path => + { + try + { + var yaml = Build.ReadFileSystem.File.ReadAllText(path); + return YamlSerialization.Deserialize(yaml, Build.ProductsConfiguration); + } + catch + { + return null; + } + }); + + if (config?.Products is null) + return null; + return config.Products.TryGetValue(productKey, out var data) ? data : null; + } + + public override IEnumerable GeneratedAnchors => + string.IsNullOrWhiteSpace(Data.Id) ? [] : [Data.Id]; +} + +[YamlSerializable] +public record WhatsNewConfig +{ + [YamlMember(Alias = "products")] + public Dictionary Products { get; set; } = []; +} + +[YamlSerializable] +public record WhatsNewData +{ + [YamlMember(Alias = "title")] + public string? Title { get; set; } + + [YamlMember(Alias = "id")] + public string? Id { get; set; } + + [YamlMember(Alias = "badge")] + public string? Badge { get; set; } = "New"; + + [YamlMember(Alias = "release-links")] + public LinkCardLink[] ReleaseLinks { get; set; } = []; + + [YamlMember(Alias = "items")] + public WhatsNewItem[] Items { get; set; } = []; + + public static WhatsNewData Empty { get; } = new(); +} + +[YamlSerializable] +public record WhatsNewItem +{ + [YamlMember(Alias = "title")] + public string? Title { get; set; } + + [YamlMember(Alias = "description")] + public string? Description { get; set; } + + [YamlMember(Alias = "link")] + public string? Link { get; set; } + + [YamlMember(Alias = "meta")] + public string? Meta { get; set; } +} diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/WhatsNewView.cshtml b/src/Elastic.Markdown/Myst/Directives/Hub/WhatsNewView.cshtml new file mode 100644 index 0000000000..ea449797ec --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/WhatsNewView.cshtml @@ -0,0 +1,75 @@ +@inherits RazorSlice + +@{ + var d = Model.Data; +} + +
      +
      + @if (!string.IsNullOrWhiteSpace(d.Badge)) + { + @d.Badge + } + @if (!string.IsNullOrWhiteSpace(d.Title)) + { + @d.Title + } + @if (d.ReleaseLinks.Length > 0) + { + + Release notes: + @for (var i = 0; i < d.ReleaseLinks.Length; i++) + { + var link = d.ReleaseLinks[i]; + if (string.IsNullOrWhiteSpace(link.Url) || string.IsNullOrWhiteSpace(link.Label)) + continue; + @link.Label + @if (i < d.ReleaseLinks.Length - 1) + { + + } + } + + } +
      + @if (d.Items.Length > 0) + { + + } +
      diff --git a/src/Elastic.Markdown/Myst/Directives/Hub/WhatsNewViewModel.cs b/src/Elastic.Markdown/Myst/Directives/Hub/WhatsNewViewModel.cs new file mode 100644 index 0000000000..678e31f650 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Hub/WhatsNewViewModel.cs @@ -0,0 +1,10 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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 + +namespace Elastic.Markdown.Myst.Directives.Hub; + +public class WhatsNewViewModel : DirectiveViewModel +{ + public required WhatsNewData Data { get; init; } +} diff --git a/src/Elastic.Markdown/Myst/YamlSerialization.cs b/src/Elastic.Markdown/Myst/YamlSerialization.cs index 4f8502e75f..af1a36d2a1 100644 --- a/src/Elastic.Markdown/Myst/YamlSerialization.cs +++ b/src/Elastic.Markdown/Myst/YamlSerialization.cs @@ -42,4 +42,10 @@ public static T Deserialize(string yaml, ProductsConfiguration products) [YamlSerializable(typeof(ContributorEntry))] [YamlSerializable(typeof(ChangelogDirectiveConfigYaml))] [YamlSerializable(typeof(ChangelogDirectiveBundleConfigYaml))] +[YamlSerializable(typeof(Elastic.Markdown.Myst.Directives.Hub.LinkCardData))] +[YamlSerializable(typeof(Elastic.Markdown.Myst.Directives.Hub.LinkCardLink))] +[YamlSerializable(typeof(Elastic.Markdown.Myst.Directives.Hub.LinkCardAside))] +[YamlSerializable(typeof(Elastic.Markdown.Myst.Directives.Hub.WhatsNewData))] +[YamlSerializable(typeof(Elastic.Markdown.Myst.Directives.Hub.WhatsNewItem))] +[YamlSerializable(typeof(Elastic.Markdown.Myst.Directives.Hub.WhatsNewConfig))] public partial class DocsBuilderYamlStaticContext; diff --git a/src/Elastic.Markdown/Page/Index.cshtml b/src/Elastic.Markdown/Page/Index.cshtml index e8325b5e91..fa35743899 100644 --- a/src/Elastic.Markdown/Page/Index.cshtml +++ b/src/Elastic.Markdown/Page/Index.cshtml @@ -6,6 +6,7 @@ @using Elastic.Markdown @using Elastic.Markdown.Myst.Components @using Markdig +@using Elastic.Markdown.Page @using ViewModelSerializerContext = Elastic.Markdown.Page.ViewModelSerializerContext @inherits RazorSliceHttpResult @implements IUsesLayout @@ -35,6 +36,8 @@ Previous = Model.PreviousDocument, Next = Model.NextDocument, NavigationHtml = Model.NavigationHtml, + NavV2Sections = Model.NavV2Sections, + ActiveSectionId = Model.ActiveSectionId, UrlPathPrefix = Model.UrlPathPrefix, GithubEditUrl = Model.GithubEditUrl, MarkdownUrl = Model.MarkdownUrl, @@ -88,18 +91,11 @@ } }
      - @* This way it's correctly rendered as

      text

      instead of

      text

      *@ - @(new HtmlString(Markdown.ToHtml("# " + Model.TitleRaw))) - @if (Model.AppliesTo is not null) + @if (Model.CurrentDocument.YamlFrontMatter?.Layout != MarkdownPageLayout.Hub) { -

      - @await RenderPartialAsync(ApplicableToComponent.Create(new ApplicableToViewModel - { - AppliesTo = Model.AppliesTo, - Inline = false, - VersionsConfig = Model.VersionsConfig - })) -

      + @* This way it's correctly rendered as

      text

      instead of

      text

      *@ + @(new HtmlString(Markdown.ToHtml("# " + Model.TitleRaw))) + @await RenderPartialAsync(MetadataBox.Create(Model)) } @(new HtmlString(Model.MarkdownHtml))
      diff --git a/src/Elastic.Markdown/Page/IndexViewModel.cs b/src/Elastic.Markdown/Page/IndexViewModel.cs index 44ecf22014..8d0ad79354 100644 --- a/src/Elastic.Markdown/Page/IndexViewModel.cs +++ b/src/Elastic.Markdown/Page/IndexViewModel.cs @@ -12,6 +12,7 @@ using Elastic.Documentation.Configuration.Toc; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Navigation; +using Elastic.Documentation.Navigation.V2; using Elastic.Documentation.Site; using Elastic.Documentation.Site.FileProviders; using Elastic.Markdown.IO; @@ -37,6 +38,12 @@ public class IndexViewModel public required string NavigationHtml { get; init; } + /// V2 section metadata for the secondary nav bar tabs. Null for V1 builds. + public IReadOnlyList? NavV2Sections { get; init; } + + /// The active section ID for highlighting the current tab in the secondary nav. + public string? ActiveSectionId { get; init; } + public required string? CurrentVersion { get; init; } public required string? AllVersionsUrl { get; init; } diff --git a/src/Elastic.Markdown/Page/MetadataBox.cshtml b/src/Elastic.Markdown/Page/MetadataBox.cshtml new file mode 100644 index 0000000000..4d69e8494d --- /dev/null +++ b/src/Elastic.Markdown/Page/MetadataBox.cshtml @@ -0,0 +1,90 @@ +@inherits RazorSlice +@using Elastic.Markdown.Myst.Components +@{ + var layout = Model.CurrentDocument.YamlFrontMatter?.Layout; + if (layout is not null) + return; + + var prefix = Model.UrlPathPrefix?.TrimEnd('/') ?? string.Empty; + var productHubs = Model.Products + .Where(p => !string.IsNullOrWhiteSpace(p.Hub)) + .OrderBy(p => p.DisplayName, StringComparer.Ordinal) + .ToList(); + + var hasStackVersions = false; + var hasDeployments = false; + if (Model.AppliesTo is not null) + { + hasStackVersions = Model.AppliesTo.Stack is not null + || (Model.AppliesTo.Deployment is null && Model.AppliesTo.Serverless is not null); + hasDeployments = Model.AppliesTo.Deployment is not null; + } + + var hasPills = productHubs.Count > 0; + var hasRows = hasStackVersions || hasDeployments; + if (!hasPills && !hasRows) + return; +} + diff --git a/src/Elastic.Markdown/Page/PlaceholderPage.cshtml b/src/Elastic.Markdown/Page/PlaceholderPage.cshtml new file mode 100644 index 0000000000..d70b34c1c8 --- /dev/null +++ b/src/Elastic.Markdown/Page/PlaceholderPage.cshtml @@ -0,0 +1,9 @@ +@inherits RazorSlice +@implements IUsesLayout +@functions { + public Elastic.Markdown.MarkdownLayoutViewModel LayoutModel => Model; +} +
      +

      @Model.Title

      +

      This content is coming soon.

      +
      diff --git a/src/Elastic.Markdown/_Layout.cshtml b/src/Elastic.Markdown/_Layout.cshtml index 398141bfbd..20974384c7 100644 --- a/src/Elastic.Markdown/_Layout.cshtml +++ b/src/Elastic.Markdown/_Layout.cshtml @@ -22,7 +22,7 @@ private async Task RenderDefault() {
      - +
      +
      +
      + @await RenderPartialAsync(_PagesNav.Create(Model)) +
      + +
      + @await RenderBodyAsync() +
      +
      + @* Empty TOC slot so htmx hx-select-oob="#toc-nav" swaps don't fail when boosted from a regular page. Hub pages have their own inline {on-this-page} chip. *@ + +
      +
      +
      + } } @if (RenderHeaderAndFooter) @@ -79,6 +98,9 @@ case MarkdownPageLayout.FullSearch: await RenderPartialAsync(_FullSearch.Create(Model)); break; + case MarkdownPageLayout.Hub: + await RenderHub(); + break; default: await RenderDefault(); break; diff --git a/src/services/Elastic.Documentation.Assembler/Building/AssemblerBuildService.cs b/src/services/Elastic.Documentation.Assembler/Building/AssemblerBuildService.cs index bc9615e18d..3dcb4ed477 100644 --- a/src/services/Elastic.Documentation.Assembler/Building/AssemblerBuildService.cs +++ b/src/services/Elastic.Documentation.Assembler/Building/AssemblerBuildService.cs @@ -9,11 +9,14 @@ using Elastic.Documentation.Assembler.Sourcing; using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.Builder; using Elastic.Documentation.Configuration.Toc; using Elastic.Documentation.Diagnostics; using Elastic.Documentation.LegacyDocs; using Elastic.Documentation.Navigation.Assembler; +using Elastic.Documentation.Navigation.V2; using Elastic.Documentation.Services; +using Elastic.Documentation.Site.FileProviders; using Microsoft.Extensions.Logging; using Nullean.ScopedFileSystem; @@ -107,11 +110,29 @@ Cancel ctx var navigationFileInfo = configurationContext.ConfigurationFileProvider.NavigationFile; var siteNavigationFile = SiteNavigationFile.Deserialize(await readFs.File.ReadAllTextAsync(navigationFileInfo.FullName, ctx)); var documentationSets = assembleSources.AssembleSets.Values.Select(s => s.DocumentationSet.Navigation).ToArray(); - var navigation = new SiteNavigation(siteNavigationFile, assembleContext, documentationSets, assembleContext.Environment.PathPrefix); + + // Normalise keys from assembler.yml (e.g. NAV_V2 → nav-v2) before checking flags + var featureFlags = new FeatureFlags([]); + foreach (var (key, value) in assembleContext.Environment.FeatureFlags) + featureFlags.Set(key, value); + + var navV2FileInfo = configurationContext.ConfigurationFileProvider.NavigationV2File; + SiteNavigation navigation; + if (featureFlags.NavV2Enabled && navV2FileInfo is not null) + { + _logger.LogInformation("nav-v2 feature flag enabled — loading navigation-v2.yml"); + var v2File = NavigationV2File.Deserialize(await readFs.File.ReadAllTextAsync(navV2FileInfo.FullName, ctx)); + navigation = new SiteNavigationV2(v2File, siteNavigationFile, assembleContext, documentationSets, assembleContext.Environment.PathPrefix); + } + else + { + navigation = new SiteNavigation(siteNavigationFile, assembleContext, documentationSets, assembleContext.Environment.PathPrefix); + } _logger.LogInformation("Validating navigation.yml does not contain colliding path prefixes"); - // this validates all path prefixes are unique, early exit if duplicates are detected - if (!SiteNavigationFile.ValidatePathPrefixes(assembleContext.Collector, siteNavigationFile, navigationFileInfo) || assembleContext.Collector.Errors > 0) + // Skip path prefix validation for V2 nav — prefixes are auto-derived + if (navigation is not SiteNavigationV2 && + (!SiteNavigationFile.ValidatePathPrefixes(assembleContext.Collector, siteNavigationFile, navigationFileInfo) || assembleContext.Collector.Errors > 0)) return false; var pathProvider = new GlobalNavigationPathProvider(navigation, assembleSources, assembleContext); @@ -123,6 +144,14 @@ Cancel ctx await builder.BuildAllAsync(assembleSources.AssembleSets, exporters, ctx); + if (exporters.Contains(Exporter.Html) && navigation is SiteNavigationV2 navV2) + { + var firstBuildContext = assembleSources.AssembleSets.Values.First().BuildContext; + var staticHashProvider = new StaticFileContentHashProvider(new EmbeddedOrPhysicalFileProvider(firstBuildContext)); + var placeholderWriter = new PlaceholderPageWriter(logFactory, navV2, htmlWriter, assembleContext, featureFlags, staticHashProvider); + await placeholderWriter.WriteAllAsync(ctx); + } + if (exporters.Contains(Exporter.LinkMetadata)) await cloner.WriteLinkRegistrySnapshot(checkoutResult.LinkRegistrySnapshot, ctx); diff --git a/src/services/Elastic.Documentation.Assembler/Building/PlaceholderPageWriter.cs b/src/services/Elastic.Documentation.Assembler/Building/PlaceholderPageWriter.cs new file mode 100644 index 0000000000..116266a1b2 --- /dev/null +++ b/src/services/Elastic.Documentation.Assembler/Building/PlaceholderPageWriter.cs @@ -0,0 +1,171 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// 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.Documentation; +using Elastic.Documentation.Assembler.Navigation; +using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Versions; +using Elastic.Documentation.Extensions; +using Elastic.Documentation.Navigation; +using Elastic.Documentation.Navigation.V2; +using Elastic.Documentation.Site; +using Elastic.Documentation.Site.FileProviders; +using Elastic.Documentation.Site.Navigation; +using Elastic.Markdown; +using Elastic.Markdown.Page; +using Microsoft.Extensions.Logging; +using RazorSlices; + +namespace Elastic.Documentation.Assembler.Building; + +/// +/// Generates stub HTML pages for all and +/// items in the V2 navigation tree. +/// Each stub renders the full site chrome (header, nav sidebar, footer) with an H1 +/// title and "coming soon" body, using the computed placeholder URL. +/// +public class PlaceholderPageWriter( + ILoggerFactory logFactory, + SiteNavigationV2 navigation, + GlobalNavigationHtmlWriter htmlWriter, + AssembleContext context, + FeatureFlags featureFlags, + StaticFileContentHashProvider staticFileHashProvider +) +{ + private readonly ILogger _logger = logFactory.CreateLogger(); + + public async Task WriteAllAsync(Cancel ctx) + { + var seen = new HashSet(StringComparer.OrdinalIgnoreCase); + var placeholders = CollectPlaceholders(navigation.V2NavigationItems).ToList(); + + // Group placeholders by section so we render each section's nav once + var groups = placeholders.GroupBy(p => navigation.GetSectionForUrl(p.Item.Url)); + foreach (var group in groups) + { + var section = group.Key; + // Use the first placeholder in the group to resolve the section's nav HTML + var representative = group.First().Item; + var renderResult = await htmlWriter.RenderNavigation(navigation, representative, ctx); + + foreach (var (title, navItem) in group) + { + if (!seen.Add(navItem.Url)) + continue; + await WritePlaceholderPageAsync(title, navItem, renderResult, ctx); + } + } + + _logger.LogInformation("Wrote {Count} placeholder pages", seen.Count); + } + + private static IEnumerable<(string Title, ILeafNavigationItem Item)> CollectPlaceholders( + IReadOnlyList items + ) + { + foreach (var item in items) + { + switch (item) + { + case PlaceholderNavigationLeaf leaf: + yield return (leaf.NavigationTitle, leaf); + break; + case PlaceholderNavigationNode group: + yield return (group.NavigationTitle, group.Index); + foreach (var child in CollectPlaceholders(group.NavigationItems.ToList())) + yield return child; + break; + case LabelNavigationNode label: + foreach (var child in CollectPlaceholders(label.NavigationItems.ToList())) + yield return child; + break; + case INodeNavigationItem node: + foreach (var child in CollectPlaceholders(node.NavigationItems.ToList())) + yield return child; + break; + } + } + } + + private async Task WritePlaceholderPageAsync( + string title, + ILeafNavigationItem navItem, + NavigationRenderResult renderResult, + Cancel ctx + ) + { + var model = CreateViewModel(title, navItem, renderResult); + var slice = PlaceholderPage.Create(model); + var html = await slice.RenderAsync(cancellationToken: ctx); + + // item.Url is like "/docs/_placeholder/AABBCCDD" — write to outputDir + url + /index.html + var relativePath = navItem.Url.TrimStart('/'); + var outputPath = Path.Combine(context.OutputDirectory.FullName, relativePath, "index.html"); + var outputDir = Path.GetDirectoryName(outputPath)!; + if (!context.WriteFileSystem.Directory.Exists(outputDir)) + _ = context.WriteFileSystem.Directory.CreateDirectory(outputDir); + + await context.WriteFileSystem.File.WriteAllTextAsync(outputPath, html, ctx); + } + + private MarkdownLayoutViewModel CreateViewModel( + string title, + ILeafNavigationItem navItem, + NavigationRenderResult renderResult + ) + { + var env = context.Environment; + var rawPrefix = env.PathPrefix; + var urlPathPrefix = string.IsNullOrEmpty(rawPrefix) ? string.Empty : $"/{rawPrefix.TrimStart('/')}"; + var rootPath = string.IsNullOrEmpty(urlPathPrefix) ? "/" : $"{urlPathPrefix.TrimEnd('/')}/"; + + return new MarkdownLayoutViewModel + { + DocsBuilderVersion = ShortId.Create(BuildContext.Version), + DocSetName = "Elastic Docs", + Title = title, + Description = string.Empty, + CurrentNavigationItem = navItem, + Previous = null, + Next = null, + NavigationHtml = renderResult.Html, + NavV2Sections = renderResult.Sections, + ActiveSectionId = renderResult.ActiveSectionId, + UrlPathPrefix = urlPathPrefix, + Htmx = new DefaultHtmxAttributeProvider(rootPath), + CanonicalBaseUrl = context.CanonicalBaseUrl, + Features = featureFlags, + GoogleTagManager = new GoogleTagManagerConfiguration + { + Enabled = env.GoogleTagManager.Enabled, + Id = env.GoogleTagManager.Id, + Auth = env.GoogleTagManager.Auth, + Preview = env.GoogleTagManager.Preview, + CookiesWin = env.GoogleTagManager.CookiesWin + }, + Optimizely = new OptimizelyConfiguration + { + Enabled = env.Optimizely.Enabled, + Id = env.Optimizely.Id + }, + AllowIndexing = false, + StaticFileContentHashProvider = staticFileHashProvider, + BuildType = BuildType.Assembler, + GithubEditUrl = null, + MarkdownUrl = string.Empty, + HideEditThisPage = true, + ReportIssueUrl = null, + Breadcrumbs = [], + PageTocItems = [], + Layout = null, + VersioningSystem = context.VersionsConfiguration.GetVersioningSystem(VersioningSystemId.All), + VersionDropdownSerializedModel = null, + CurrentVersion = null, + AllVersionsUrl = null + }; + } +} diff --git a/src/services/Elastic.Documentation.Assembler/ContentSources/RepositoryPublishValidationService.cs b/src/services/Elastic.Documentation.Assembler/ContentSources/RepositoryPublishValidationService.cs index 8f11b65b68..0c67cc354d 100644 --- a/src/services/Elastic.Documentation.Assembler/ContentSources/RepositoryPublishValidationService.cs +++ b/src/services/Elastic.Documentation.Assembler/ContentSources/RepositoryPublishValidationService.cs @@ -47,13 +47,13 @@ public async Task ValidatePublishStatus(IDiagnosticsCollector collector, C var next = repository.GetBranch(ContentSource.Next); if (!registryMapping.TryGetValue(next, out _)) { - collector.EmitError(reportPath, + collector.EmitWarning(reportPath, $"'{repository.Name}' has not yet published links.json for configured 'next' content source: '{next}' see {linkIndexReader.RegistryUrl}"); } if (!registryMapping.TryGetValue(current, out _)) { - collector.EmitError(reportPath, + collector.EmitWarning(reportPath, $"'{repository.Name}' has not yet published links.json for configured 'current' content source: '{current}' see {linkIndexReader.RegistryUrl}"); } } diff --git a/src/services/Elastic.Documentation.Assembler/Navigation/GlobalNavigationHtmlWriter.cs b/src/services/Elastic.Documentation.Assembler/Navigation/GlobalNavigationHtmlWriter.cs index a833213cc3..caef5c711d 100644 --- a/src/services/Elastic.Documentation.Assembler/Navigation/GlobalNavigationHtmlWriter.cs +++ b/src/services/Elastic.Documentation.Assembler/Navigation/GlobalNavigationHtmlWriter.cs @@ -7,6 +7,7 @@ using Elastic.Documentation.Diagnostics; using Elastic.Documentation.Navigation; using Elastic.Documentation.Navigation.Assembler; +using Elastic.Documentation.Navigation.V2; using Elastic.Documentation.Site; using Elastic.Documentation.Site.Navigation; using Microsoft.Extensions.Logging; @@ -22,12 +23,14 @@ public class GlobalNavigationHtmlWriter(ILoggerFactory logFactory, SiteNavigatio public async Task RenderNavigation( IRootNavigationItem currentRootNavigation, -#pragma warning disable IDE0060 - INavigationItem currentNavigationItem, // temporary https://github.com/elastic/docs-content/pull/3730 -#pragma warning restore IDE0060 + INavigationItem currentNavigationItem, Cancel ctx = default ) { + // V2 nav: render per-section sidebar based on current page + if (globalNavigation is SiteNavigationV2 navV2) + return await RenderSectionNavigation(navV2, currentRootNavigation, currentNavigationItem, ctx); + if (currentRootNavigation is SiteNavigation) return NavigationRenderResult.Empty; @@ -64,6 +67,175 @@ public async Task RenderNavigation( } } + private async Task RenderSectionNavigation( + SiteNavigationV2 navV2, + IRootNavigationItem currentRootNavigation, + INavigationItem currentNavigationItem, + Cancel ctx + ) + { + // Islands take priority: if the current toc root is an island, render the island sidebar + var island = navV2.GetIslandForTocRoot(currentRootNavigation); + if (island is not null) + return await RenderIslandNavigation(island, navV2, ctx); + + var section = navV2.GetSectionForUrl(currentNavigationItem.Url); + if (section is null) + return await RenderFullV2Navigation(navV2, ctx); + + var cacheKey = $"nav-v2-section-{section.Id}"; + if (_renderedNavigationCache.TryGetValue(cacheKey, out var cachedHtml)) + return CreateSectionResult(cachedHtml, section, navV2); + + await _semaphore.WaitAsync(ctx); + try + { + if (_renderedNavigationCache.TryGetValue(cacheKey, out cachedHtml)) + return CreateSectionResult(cachedHtml, section, navV2); + + _logger.LogInformation("Rendering V2 section navigation: {SectionLabel} ({SectionId})", section.Label, section.Id); + + var wrapper = new SectionNavigationV2Wrapper(section, navV2); + var model = new NavigationViewModel + { + Title = section.Label, + TitleUrl = section.Url, + Tree = wrapper, + IsPrimaryNavEnabled = true, + IsUsingNavigationDropdown = false, + IsGlobalAssemblyBuild = true, + TopLevelItems = [], + Htmx = new DefaultHtmxAttributeProvider("/"), + BuildType = BuildType.Assembler, + IsNavV2 = true, + IsIsolatedSection = section.Isolated, + SectionUrl = CombineWithSitePrefix(navV2, section.Url) + }; + + var html = await ((INavigationHtmlWriter)this).Render(model, ctx); + _renderedNavigationCache[cacheKey] = html; + return CreateSectionResult(html, section, navV2); + } + finally + { + _ = _semaphore.Release(); + } + } + + private async Task RenderIslandNavigation( + NavigationIsland island, + SiteNavigationV2 navV2, + Cancel ctx + ) + { + var cacheKey = $"nav-v2-island-{island.Id}"; + if (_renderedNavigationCache.TryGetValue(cacheKey, out var cachedHtml)) + return CreateIslandResult(cachedHtml, island, navV2); + + await _semaphore.WaitAsync(ctx); + try + { + if (_renderedNavigationCache.TryGetValue(cacheKey, out cachedHtml)) + return CreateIslandResult(cachedHtml, island, navV2); + + _logger.LogInformation("Rendering V2 island navigation: {IslandLabel} ({IslandId})", island.Label, island.Id); + + var wrapper = new SectionNavigationV2Wrapper( + new NavigationSection(island.Id, island.Label, "", false, false, island.NavigationItems), + navV2 + ); + var model = new NavigationViewModel + { + Title = island.Label, + TitleUrl = "", + Tree = wrapper, + IsPrimaryNavEnabled = true, + IsUsingNavigationDropdown = false, + IsGlobalAssemblyBuild = true, + TopLevelItems = [], + Htmx = new DefaultHtmxAttributeProvider("/"), + BuildType = BuildType.Assembler, + IsNavV2 = true, + IsIsolatedSection = true, + SectionUrl = null, + BackArrowUrl = CombineWithSitePrefix(navV2, island.ParentSection.Url) + }; + + var html = await ((INavigationHtmlWriter)this).Render(model, ctx); + _renderedNavigationCache[cacheKey] = html; + return CreateIslandResult(html, island, navV2); + } + finally + { + _ = _semaphore.Release(); + } + } + + private static NavigationRenderResult CreateIslandResult(string html, NavigationIsland island, SiteNavigationV2 navV2) => + new() + { + Html = html, + Id = $"nav-v2-island-{island.Id}", + Sections = navV2.Sections, + ActiveSectionId = island.ParentSection.Id + }; + + private static string CombineWithSitePrefix(SiteNavigation nav, string sectionUrl) + { + var prefix = nav.Url.TrimEnd('/'); + var path = sectionUrl.TrimStart('/'); + return string.IsNullOrEmpty(path) ? $"{prefix}/" : $"{prefix}/{path}"; + } + + private static NavigationRenderResult CreateSectionResult(string html, NavigationSection activeSection, SiteNavigationV2 navV2) => + new() + { + Html = html, + Id = $"nav-v2-section-{activeSection.Id}", + Sections = navV2.Sections, + ActiveSectionId = activeSection.Id + }; + + /// Fallback when no section: items exist — renders the full V2 tree as before. + private async Task RenderFullV2Navigation(SiteNavigationV2 navV2, Cancel ctx) + { + const string cacheKey = "nav-v2"; + if (_renderedNavigationCache.TryGetValue(cacheKey, out var cachedHtml)) + return new NavigationRenderResult { Html = cachedHtml, Id = cacheKey }; + + await _semaphore.WaitAsync(ctx); + try + { + if (_renderedNavigationCache.TryGetValue(cacheKey, out cachedHtml)) + return new NavigationRenderResult { Html = cachedHtml, Id = cacheKey }; + + _logger.LogInformation("Rendering V2 navigation (full tree fallback)"); + + var syntheticV2Root = new FullV2Wrapper(navV2); + var model = new NavigationViewModel + { + Title = "Elastic Docs", + TitleUrl = navV2.Url, + Tree = syntheticV2Root, + IsPrimaryNavEnabled = true, + IsUsingNavigationDropdown = false, + IsGlobalAssemblyBuild = true, + TopLevelItems = [], + Htmx = new DefaultHtmxAttributeProvider("/"), + BuildType = BuildType.Assembler, + IsNavV2 = true + }; + + var html = await ((INavigationHtmlWriter)this).Render(model, ctx); + _renderedNavigationCache[cacheKey] = html; + return new NavigationRenderResult { Html = html, Id = cacheKey }; + } + finally + { + _ = _semaphore.Release(); + } + } + private NavigationViewModel CreateNavigationModel(INodeNavigationItem group) { var topLevelItems = globalNavigation.TopLevelItems; @@ -86,4 +258,34 @@ public void Dispose() _semaphore.Dispose(); GC.SuppressFinalize(this); } + + /// Wraps a single as the sidebar tree root. + private sealed class SectionNavigationV2Wrapper(NavigationSection section, SiteNavigationV2 navV2) + : INodeNavigationItem + { + public string Id => section.Id; + public string Url => section.Url; + public string NavigationTitle => section.Label; + public IRootNavigationItem NavigationRoot => navV2; + public INodeNavigationItem? Parent { get; set; } + public bool Hidden => false; + public int NavigationIndex { get; set; } + public ILeafNavigationItem Index => navV2.Index; + public IReadOnlyCollection NavigationItems => section.NavigationItems; + } + + /// Fallback wrapper exposing the full V2 tree (used when no sections are defined). + private sealed class FullV2Wrapper(SiteNavigationV2 navV2) + : INodeNavigationItem + { + public string Id => "nav-v2-root"; + public string Url => navV2.Url; + public string NavigationTitle => navV2.NavigationTitle; + public IRootNavigationItem NavigationRoot => navV2; + public INodeNavigationItem? Parent { get; set; } + public bool Hidden => false; + public int NavigationIndex { get; set; } + public ILeafNavigationItem Index => navV2.Index; + public IReadOnlyCollection NavigationItems => navV2.V2NavigationItems; + } } diff --git a/src/services/Elastic.Documentation.Assembler/Sourcing/RepositorySourcesFetcher.cs b/src/services/Elastic.Documentation.Assembler/Sourcing/RepositorySourcesFetcher.cs index f53f37c40d..d2b01e5b61 100644 --- a/src/services/Elastic.Documentation.Assembler/Sourcing/RepositorySourcesFetcher.cs +++ b/src/services/Elastic.Documentation.Assembler/Sourcing/RepositorySourcesFetcher.cs @@ -97,10 +97,13 @@ await Task.Run(() => { if (!entry.TryGetValue(branch, out var entryInfo)) { - context.Collector.EmitError("", $"'{repo.Key}' does not have a '{branch}' entry in link index"); - return; + // Branch has no published link index (e.g. a prototype or dev branch). + // Warn and check out the branch directly without a pinned git ref; + // cross-link resolution will use whatever index entries are available. + context.Collector.EmitWarning("", $"'{repo.Key}' does not have a '{branch}' entry in link index, checking out branch directly"); } - gitRef = entryInfo.GitReference; + else + gitRef = entryInfo.GitReference; } var cloneInformation = RepositorySourcer.CloneRef(repo.Value, gitRef, fetchLatest, assumeCloned: assumeCloned); diff --git a/src/tooling/docs-builder/Http/DocumentationWebHost.cs b/src/tooling/docs-builder/Http/DocumentationWebHost.cs index 42c1c52499..e96c39c313 100644 --- a/src/tooling/docs-builder/Http/DocumentationWebHost.cs +++ b/src/tooling/docs-builder/Http/DocumentationWebHost.cs @@ -71,6 +71,7 @@ bool isWatchBuild Context = new BuildContext(collector, readFs, writeFs, configurationContext, ExportOptions.Default, path, null) { CanonicalBaseUrl = new Uri(hostUrl), + BuildType = BuildType.Isolated, }; // Enable diagnostics panel in serve mode diff --git a/src/tooling/docs-builder/Http/StaticWebHost.cs b/src/tooling/docs-builder/Http/StaticWebHost.cs index 788509e6bc..06d16b0da2 100644 --- a/src/tooling/docs-builder/Http/StaticWebHost.cs +++ b/src/tooling/docs-builder/Http/StaticWebHost.cs @@ -111,6 +111,12 @@ private async Task ServeDocumentationFile(string slug, Cancel _) return Results.NotFound(); await Task.CompletedTask; + +#if DEBUG + if (TryResolveDevSiteStaticFile(slug, out var devStatic)) + return Results.File(devStatic.FullName, GetStaticMimeType(devStatic.Extension)); +#endif + var contentRoot = Path.GetFullPath(_contentRoot); var localPath = Path.GetFullPath(Path.Join(contentRoot, slug.Replace('/', Path.DirectorySeparatorChar))); if (!localPath.StartsWith(contentRoot + Path.DirectorySeparatorChar, StringComparison.Ordinal)) @@ -121,28 +127,60 @@ private async Task ServeDocumentationFile(string slug, Cancel _) fileInfo = new FileInfo(Path.Join(directoryInfo.FullName, "index.html")); if (fileInfo.Exists) - { - var mimetype = fileInfo.Extension switch - { - ".js" => "text/javascript", - ".css" => "text/css", - ".png" => "image/png", - ".jpg" => "image/jpeg", - ".gif" => "image/gif", - ".svg" => "image/svg+xml", - ".ico" => "image/x-icon", - ".json" => "application/json", - ".map" => "application/json", - ".txt" => "text/plain", - ".xml" => "text/xml", - ".yml" => "text/yaml", - ".md" => "text/markdown", - _ => "text/html" - }; - return Results.File(fileInfo.FullName, mimetype); - } - + return Results.File(fileInfo.FullName, GetStaticMimeType(fileInfo.Extension)); return Results.NotFound(); } + + private static string GetStaticMimeType(string extension) => + extension switch + { + ".js" => "text/javascript", + ".css" => "text/css", + ".png" => "image/png", + ".jpg" => "image/jpeg", + ".gif" => "image/gif", + ".svg" => "image/svg+xml", + ".ico" => "image/x-icon", + ".json" => "application/json", + ".map" => "application/json", + ".txt" => "text/plain", + ".xml" => "text/xml", + ".yml" => "text/yaml", + ".md" => "text/markdown", + _ => "text/html" + }; + +#if DEBUG + /// + /// assembler serve reads pre-built HTML from .artifacts/assembly; static assets there lag behind + /// src/Elastic.Documentation.Site/_static until assembler build runs. In Debug, prefer the live Parcel output when present. + /// + private static bool TryResolveDevSiteStaticFile(string slug, out FileInfo devStatic) + { + devStatic = null!; + const string marker = "_static/"; + var idx = slug.IndexOf(marker, StringComparison.Ordinal); + if (idx < 0) + return false; + + var solutionRoot = Paths.GetSolutionDirectory(); + if (solutionRoot is null) + return false; + + var relativeWithinStatic = slug[(idx + marker.Length)..].Replace('/', Path.DirectorySeparatorChar); + var devStaticRoot = Path.GetFullPath(Path.Join(solutionRoot.FullName, "src", "Elastic.Documentation.Site", "_static")); + var candidatePath = Path.GetFullPath(Path.Join(devStaticRoot, relativeWithinStatic)); + var devPrefix = devStaticRoot + Path.DirectorySeparatorChar; + if (!candidatePath.StartsWith(devPrefix, StringComparison.Ordinal) && !string.Equals(candidatePath, devStaticRoot, StringComparison.Ordinal)) + return false; + + var candidate = new FileInfo(candidatePath); + if (!candidate.Exists) + return false; + + devStatic = candidate; + return true; + } +#endif } diff --git a/tests/Elastic.Markdown.Tests/Assembler/AssemblerHtmxMarkdownLinkTests.cs b/tests/Elastic.Markdown.Tests/Assembler/AssemblerHtmxMarkdownLinkTests.cs index 9698c3f306..d031a35420 100644 --- a/tests/Elastic.Markdown.Tests/Assembler/AssemblerHtmxMarkdownLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Assembler/AssemblerHtmxMarkdownLinkTests.cs @@ -29,7 +29,7 @@ protected override BuildContext CreateBuildContext( [Fact] public void CrossLink_UsesGranularSwap_ForAssembler() => - Html.Should().Contain("hx-select-oob=\"#content-container,#toc-nav,#nav-tree,#nav-dropdown\""); + Html.Should().Contain("hx-select-oob=\"#content-container,#toc-nav,#pages-nav\""); [Fact] public void CrossLink_HasPreload() => @@ -98,7 +98,7 @@ protected override BuildContext CreateBuildContext( public void AbsolutePathLink_GetsHtmxAttributes_ForAssembler() { // Assembler: absolute path links get HTMX (granular swap when hasSameTopLevelGroup is false) - Html.Should().Contain("hx-select-oob=\"#content-container,#toc-nav,#nav-tree,#nav-dropdown\""); + Html.Should().Contain("hx-select-oob=\"#content-container,#toc-nav,#pages-nav\""); Html.Should().Contain("preload=\"mousedown\""); } @@ -158,7 +158,7 @@ protected override BuildContext CreateBuildContext( [Fact] public void EmptyTextCrossLink_UsesGranularSwap_ForAssembler() => - Html.Should().Contain("hx-select-oob=\"#content-container,#toc-nav,#nav-tree,#nav-dropdown\""); + Html.Should().Contain("hx-select-oob=\"#content-container,#toc-nav,#pages-nav\""); [Fact] public void EmptyTextCrossLink_NoTargetBlank() => diff --git a/tests/Navigation.Tests/Assembler/SiteNavigationTests.cs b/tests/Navigation.Tests/Assembler/SiteNavigationTests.cs index c79316636d..27daf3f7cb 100644 --- a/tests/Navigation.Tests/Assembler/SiteNavigationTests.cs +++ b/tests/Navigation.Tests/Assembler/SiteNavigationTests.cs @@ -10,6 +10,7 @@ using Elastic.Documentation.Navigation.Isolated; using Elastic.Documentation.Navigation.Isolated.Node; using Elastic.Documentation.Navigation.Tests.Isolation; +using Elastic.Documentation.Navigation.V2; namespace Elastic.Documentation.Navigation.Tests.Assembler; @@ -305,4 +306,142 @@ public void SitePrefixAppliedToMultipleNavigationItems(string? sitePrefix, strin var searchItem = navigation.NavigationItems.Skip(1).First(); searchItem.Url.Should().Be(expectedSearchUrl, $"search URL should be '{expectedSearchUrl}' with sitePrefix '{sitePrefix}'"); } + + [Fact] + public void SiteNavigationV2PageEntriesResolveFilesFromUnseenChildTocs() + { + var fileSystem = new MockFileSystem(); + var docsContentDir = "/checkouts/current/docs-content"; + + // language=yaml + var docsetYaml = """ + project: Docs content + toc: + - file: index.md + - toc: products + """; + fileSystem.AddFile($"{docsContentDir}/docs/docset.yml", new MockFileData(docsetYaml)); + fileSystem.AddFile($"{docsContentDir}/docs/index.md", new MockFileData("# Docs content")); + + // language=yaml + var productsTocYaml = """ + toc: + - file: index.md + - toc: elasticsearch + """; + fileSystem.AddFile($"{docsContentDir}/docs/products/toc.yml", new MockFileData(productsTocYaml)); + fileSystem.AddFile($"{docsContentDir}/docs/products/index.md", new MockFileData("# Product hubs")); + + // language=yaml + var elasticsearchTocYaml = """ + toc: + - file: v9.md + """; + fileSystem.AddFile($"{docsContentDir}/docs/products/elasticsearch/toc.yml", new MockFileData(elasticsearchTocYaml)); + fileSystem.AddFile($"{docsContentDir}/docs/products/elasticsearch/v9.md", new MockFileData("# Elasticsearch")); + + // language=yaml + var siteNavYaml = """ + toc: + - toc: products + """; + var siteNavFile = SiteNavigationFile.Deserialize(siteNavYaml); + + // language=yaml + var v2NavYaml = """ + nav: + - section: Product hubs + url: /products/ + children: + - page: docs-content://products/elasticsearch/v9.md + title: Elasticsearch + """; + var v2File = NavigationV2File.Deserialize(v2NavYaml); + + var docsContentContext = SiteNavigationTestFixture.CreateAssemblerContext(fileSystem, docsContentDir, output); + var docset = DocumentationSetFile.LoadAndResolve( + docsContentContext.Collector, + fileSystem.FileInfo.New($"{docsContentDir}/docs/docset.yml"), + FileSystemFactory.ScopeSourceDirectory(fileSystem, "/checkouts") + ); + var docsContentNav = new DocumentationSetNavigation( + docset, + docsContentContext, + GenericDocumentationFileFactory.Instance + ); + + var siteContext = SiteNavigationTestFixture.CreateAssemblerContext(fileSystem, docsContentDir, output); + var navigation = new SiteNavigationV2(v2File, siteNavFile, siteContext, [docsContentNav], sitePrefix: "docs"); + + var elasticsearchToc = navigation.Nodes[new Uri("docs-content://products/elasticsearch")]; + var elasticsearchFile = elasticsearchToc.Index.Model; + + navigation.NavigationDocumentationFileLookup.TryGetValue(elasticsearchFile, out var navigationItem) + .Should().BeTrue("V2 page entries should map docs files even when their child TOC is not declared in navigation.yml"); + navigationItem.Should().BeOfType(); + navigationItem.Url.Should().Be("/docs/products/elasticsearch/v9"); + } + + [Fact] + public void SiteNavigationResolvesFilesFromUnseenChildTocs() + { + var fileSystem = new MockFileSystem(); + var docsContentDir = "/checkouts/current/docs-content"; + + // language=yaml + var docsetYaml = """ + project: Docs content + toc: + - file: index.md + - toc: products + """; + fileSystem.AddFile($"{docsContentDir}/docs/docset.yml", new MockFileData(docsetYaml)); + fileSystem.AddFile($"{docsContentDir}/docs/index.md", new MockFileData("# Docs content")); + + // language=yaml + var productsTocYaml = """ + toc: + - file: index.md + - toc: elasticsearch + """; + fileSystem.AddFile($"{docsContentDir}/docs/products/toc.yml", new MockFileData(productsTocYaml)); + fileSystem.AddFile($"{docsContentDir}/docs/products/index.md", new MockFileData("# Product hubs")); + + // language=yaml + var elasticsearchTocYaml = """ + toc: + - file: v9.md + """; + fileSystem.AddFile($"{docsContentDir}/docs/products/elasticsearch/toc.yml", new MockFileData(elasticsearchTocYaml)); + fileSystem.AddFile($"{docsContentDir}/docs/products/elasticsearch/v9.md", new MockFileData("# Elasticsearch")); + + // language=yaml + var siteNavYaml = """ + toc: + - toc: products + """; + var siteNavFile = SiteNavigationFile.Deserialize(siteNavYaml); + + var docsContentContext = SiteNavigationTestFixture.CreateAssemblerContext(fileSystem, docsContentDir, output); + var docset = DocumentationSetFile.LoadAndResolve( + docsContentContext.Collector, + fileSystem.FileInfo.New($"{docsContentDir}/docs/docset.yml"), + FileSystemFactory.ScopeSourceDirectory(fileSystem, "/checkouts") + ); + var docsContentNav = new DocumentationSetNavigation( + docset, + docsContentContext, + GenericDocumentationFileFactory.Instance + ); + + var siteContext = SiteNavigationTestFixture.CreateAssemblerContext(fileSystem, docsContentDir, output); + var navigation = new SiteNavigation(siteNavFile, siteContext, [docsContentNav], sitePrefix: "docs"); + + var elasticsearchToc = navigation.Nodes[new Uri("docs-content://products/elasticsearch")]; + var elasticsearchFile = elasticsearchToc.Index.Model; + + navigation.NavigationDocumentationFileLookup.TryGetValue(elasticsearchFile, out var navigationItem) + .Should().BeTrue("unseen child TOCs can still own files that the assembler processes"); + navigationItem.Should().BeSameAs(elasticsearchToc); + } } diff --git a/tests/authoring/Applicability/ApplicableToComponent.fs b/tests/authoring/Applicability/ApplicableToComponent.fs index b6970a7828..c1ad9ae8ac 100644 --- a/tests/authoring/Applicability/ApplicableToComponent.fs +++ b/tests/authoring/Applicability/ApplicableToComponent.fs @@ -31,7 +31,7 @@ stack: ga 9.0.0 let ``renders GA with version`` () = markdown |> convertsToHtml """

      - +

      """ @@ -47,7 +47,7 @@ stack: preview 9.1.0 let ``renders preview future version as planned`` () = markdown |> convertsToHtml """

      - +

      """ @@ -63,7 +63,7 @@ stack: beta 8.8.0 let ``renders beta future version`` () = markdown |> convertsToHtml """

      - +

      """ @@ -79,7 +79,7 @@ stack: deprecated 8.7.0 let ``renders deprecation planned`` () = markdown |> convertsToHtml """

      - +

      """ @@ -95,7 +95,7 @@ stack: removed 8.6.0 let ``renders planned for removal`` () = markdown |> convertsToHtml """

      - +

      """ @@ -111,7 +111,7 @@ stack: ga let ``renders ga without version in badge or popover`` () = markdown |> convertsToHtml """

      - +

      """ @@ -128,7 +128,7 @@ serverless: ga let ``renders serverless ga`` () = markdown |> convertsToHtml """

      - +

      """ @@ -146,11 +146,11 @@ serverless: let ``renders serverless individual projects`` () = markdown |> convertsToHtml """

      - + - + - +

      """ @@ -292,7 +292,7 @@ stack: ga 8.8.0, preview 8.1.0 let ``renders Planned when GA and Preview are both unreleased`` () = markdown |> convertsToHtml """

      - +

      """ @@ -308,7 +308,7 @@ stack: deprecated 9.1.0 let ``renders deprecation planned for future version`` () = markdown |> convertsToHtml """

      - +

      """ @@ -324,7 +324,7 @@ stack: removed 9.1.0 let ``renders removal planned for future version`` () = markdown |> convertsToHtml """

      - +

      """ @@ -341,7 +341,7 @@ stack: unavailable let ``renders unavailable`` () = markdown |> convertsToHtml """

      - +

      """ @@ -398,11 +398,11 @@ apm_agent_java: beta 9.1.0 let ``renders complex mixed scenario`` () = markdown |> convertsToHtml """

      - + - + - + @@ -428,7 +428,7 @@ deployment: let ``renders stack and ece planned`` () = markdown |> convertsToHtml """

      - + @@ -446,7 +446,7 @@ stack: let ``no version defaults to ga`` () = markdown |> convertsToHtml """

      - +

      """ @@ -470,8 +470,8 @@ product: ga 9.0.0 let ``renders VersioningSystemId coverage`` () = markdown |> convertsToHtml """

      - - + + @@ -493,7 +493,7 @@ stack: ga 8.0.0, beta 8.1.0 let ``renders multiple lifecycles with ellipsis and shows GA lifecycle`` () = markdown |> convertsToHtml """

      - +

      """ @@ -509,7 +509,7 @@ stack: ga 7.0.0 let ``renders ga since released version`` () = markdown |> convertsToHtml """

      - +

      """ @@ -525,7 +525,7 @@ stack: preview 7.0.0 let ``renders preview since released version`` () = markdown |> convertsToHtml """

      - +

      """ @@ -541,7 +541,7 @@ stack: beta 7.0.0 let ``renders beta since released version`` () = markdown |> convertsToHtml """

      - +

      """ @@ -557,7 +557,7 @@ stack: deprecated 7.0.0 let ``renders deprecated since released version`` () = markdown |> convertsToHtml """

      - +

      """ @@ -573,7 +573,7 @@ stack: removed 7.0.0 let ``renders removed in released version`` () = markdown |> convertsToHtml """

      - +

      """ @@ -590,7 +590,7 @@ stack: ga =7.5 let ``renders ga in exact released version`` () = markdown |> convertsToHtml """

      - +

      """ @@ -606,7 +606,7 @@ stack: ga 7.0-8.0 let ``renders ga from-to when both ends released`` () = markdown |> convertsToHtml """

      - +

      """ @@ -622,7 +622,7 @@ stack: ga 7.0-9.0 let ``renders ga since min when max unreleased`` () = markdown |> convertsToHtml """

      - +

      """ @@ -639,7 +639,7 @@ stack: preview 7.0, ga 7.5 let ``renders ga badge with both lifecycles in popover`` () = markdown |> convertsToHtml """

      - +

      """ @@ -655,7 +655,7 @@ stack: preview 7.5.4! let ``renders patch version when explicitly requested with exclamation mark`` () = markdown |> convertsToHtml """

      - +

      """ @@ -671,7 +671,7 @@ stack: preview 7.5.4 let ``hides patch version when no exclamation mark used`` () = markdown |> convertsToHtml """

      - +

      """ @@ -687,7 +687,7 @@ stack: beta 7.0.3!-7.5.2! let ``renders range with patch versions when both have exclamation marks`` () = markdown |> convertsToHtml """

      - +

      """ @@ -703,7 +703,7 @@ stack: ga 7.0.5!-7.5 let ``renders range with patch on mininum version only when explicit operator is used`` () = markdown |> convertsToHtml """

      - +

      """ @@ -719,7 +719,7 @@ stack: ga 7.0-7.5.3! let ``renders range with patch on maxinum version only when explicit operator is used`` () = markdown |> convertsToHtml """

      - +

      """ @@ -735,7 +735,7 @@ stack: ga =7.5.3! let ``renders exact version with patch when explicit operator is used`` () = markdown |> convertsToHtml """

      - +

      """ diff --git a/tests/authoring/Blocks/Admonitions.fs b/tests/authoring/Blocks/Admonitions.fs index 7d0585d504..719fa637d0 100644 --- a/tests/authoring/Blocks/Admonitions.fs +++ b/tests/authoring/Blocks/Admonitions.fs @@ -64,7 +64,7 @@ This is a custom admonition with applies_to information.
      Note - +
      @@ -76,7 +76,7 @@ This is a custom admonition with applies_to information.
      Warning - +
      @@ -88,7 +88,7 @@ This is a custom admonition with applies_to information.
      Tip - +
      diff --git a/tests/authoring/Framework/HtmlAssertions.fs b/tests/authoring/Framework/HtmlAssertions.fs index eaa648d322..0f025cfe01 100644 --- a/tests/authoring/Framework/HtmlAssertions.fs +++ b/tests/authoring/Framework/HtmlAssertions.fs @@ -105,7 +105,7 @@ actual: {actual} let formatter = PrettyMarkupFormatter() element.Children |> Seq.indexed - |> Seq.filter (fun (i, c) -> (not <| (i = 0 && c.TagName = "H1"))) + |> Seq.filter (fun (i, c) -> not (c.ClassList.Contains("page-title-row")) && not (i = 0 && c.TagName = "H1")) |> Seq.map(fun (_, c) -> c) |> Seq.iter _.ToHtml(sw, formatter) sw.ToString().TrimStart('\n') diff --git a/tests/authoring/Inline/AppliesToRole.fs b/tests/authoring/Inline/AppliesToRole.fs index 81bb0f78fd..3c93c00cf3 100644 --- a/tests/authoring/Inline/AppliesToRole.fs +++ b/tests/authoring/Inline/AppliesToRole.fs @@ -30,7 +30,7 @@ This is an inline {applies_to}`stack: preview 9.1` element. markdown |> convertsToHtml """

      This is an inline - + element.

      @@ -150,7 +150,7 @@ This is an inline {applies_to}`stack: preview 8.0, ga 8.1` element. markdown |> convertsToHtml """

      This is an inline - + element.