From 00df872e0314d789cf4e787566747eb6e86ab7d2 Mon Sep 17 00:00:00 2001 From: euvre <93761161+euvre@users.noreply.github.com> Date: Fri, 14 Aug 2026 05:22:29 -0700 Subject: [PATCH] fix(search): show honest empty state when mind map has no content (#18294) --- internal/handler/mindmap.go | 2 +- internal/handler/mindmap_test.go | 72 +++++++++++++++++++++ web/src/pages/next-search/mindmap-sheet.tsx | 13 +++- 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 internal/handler/mindmap_test.go diff --git a/internal/handler/mindmap.go b/internal/handler/mindmap.go index 0b71743e82..120950c030 100644 --- a/internal/handler/mindmap.go +++ b/internal/handler/mindmap.go @@ -86,7 +86,7 @@ func runMindMap(ctx context.Context, config mindMapRunConfig) (mindMapNode, erro sb.WriteString(delta) } fullText := sb.String() - if fullText == "" { + if strings.TrimSpace(fullText) == "" { return mindMapNode{ID: "root", Children: []mindMapNode{}}, nil } return parseMindMapMarkdown(fullText), nil diff --git a/internal/handler/mindmap_test.go b/internal/handler/mindmap_test.go new file mode 100644 index 0000000000..da602b0990 --- /dev/null +++ b/internal/handler/mindmap_test.go @@ -0,0 +1,72 @@ +// +// Copyright 2026 The InfiniFlow Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package handler + +import "testing" + +// The mind map contract: when there is nothing to map, the backend returns an +// honest empty tree (root with no children) and the frontend renders an empty +// state. No synthetic nodes are fabricated. +func TestParseMindMapMarkdownEmpty(t *testing.T) { + t.Run("prose without headings or list items yields an empty tree", func(t *testing.T) { + node := parseMindMapMarkdown("I cannot summarize this text right now.") + if node.ID != "root" || len(node.Children) != 0 { + t.Fatalf("expected root-only tree, got %+v", node) + } + }) + + t.Run("think-only answer yields an empty tree", func(t *testing.T) { + node := parseMindMapMarkdown("reasoning without any outline") + if node.ID != "root" || len(node.Children) != 0 { + t.Fatalf("expected root-only tree, got %+v", node) + } + }) + + t.Run("blank answer yields an empty tree", func(t *testing.T) { + node := parseMindMapMarkdown(" \n\n") + if node.ID != "root" || len(node.Children) != 0 { + t.Fatalf("expected root-only tree, got %+v", node) + } + }) +} + +func TestParseMindMapMarkdownTree(t *testing.T) { + t.Run("headings and nested lists build a real tree", func(t *testing.T) { + node := parseMindMapMarkdown("# Title\n## Section A\n- point 1\n - point 1.1\n## Section B\n") + if node.ID != "Title" { + t.Fatalf("expected single top heading as root, got %+v", node) + } + if len(node.Children) != 2 { + t.Fatalf("expected 2 sections, got %+v", node.Children) + } + sectionA := node.Children[0] + if sectionA.ID != "Section A" || len(sectionA.Children) != 1 { + t.Fatalf("unexpected Section A: %+v", sectionA) + } + point := sectionA.Children[0] + if point.ID != "point 1" || len(point.Children) != 1 || point.Children[0].ID != "point 1.1" { + t.Fatalf("unexpected nested list parse: %+v", point) + } + }) + + t.Run("multiple top headings keep an explicit root with children", func(t *testing.T) { + node := parseMindMapMarkdown("# Alpha\n# Beta\n") + if node.ID != "root" || len(node.Children) != 2 { + t.Fatalf("expected root with 2 children, got %+v", node) + } + }) +} diff --git a/web/src/pages/next-search/mindmap-sheet.tsx b/web/src/pages/next-search/mindmap-sheet.tsx index 25e3f4295a..b681ab84b8 100644 --- a/web/src/pages/next-search/mindmap-sheet.tsx +++ b/web/src/pages/next-search/mindmap-sheet.tsx @@ -34,6 +34,10 @@ interface IProps extends IModalProps { const MindMapSheet = ({ data, hideModal, loading, visible }: IProps) => { const { t } = useTranslation(); const percent = usePendingMindMap(); + // An empty tree (no children) means the backend honestly found nothing to + // map. Render an explicit empty state instead of a ghost "root" node. + const isEmptyMindMap = + !data || !Array.isArray(data.children) || data.children.length === 0; return ( { )} - {!loading && ( + {!loading && isEmptyMindMap && ( + + + {t('knowledgeDetails.noStructureMindmap')} + + + )} + {!loading && !isEmptyMindMap && (
+ {t('knowledgeDetails.noStructureMindmap')} +