web: fix html entities shown in match result (#9645)

* web: fix non-breaking space shown as encoded in match result
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>

* core: decode matches in db.lookup.notesWithHighlighting
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>

* core: decode entities at parse time

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
This commit is contained in:
01zulfi
2026-04-06 14:20:10 +05:00
committed by GitHub
parent d49d42b736
commit 21f4b906d7
4 changed files with 34 additions and 14 deletions

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { HighlightedResult } from "@notesnook/core";
import { HighlightedResult, Match as TMatch } from "@notesnook/core";
import { Button, Flex, Text } from "@theme-ui/components";
import React, { Fragment, useState } from "react";
import { useEditorStore } from "../../stores/editor-store";
@@ -112,11 +112,7 @@ function SearchResult(props: SearchResultProps) {
}}
>
{item.title.map((match) => (
<Fragment key={match.id}>
<span>{match.prefix}</span>
<span className="match">{match.match}</span>
{match.suffix ? <span>{match.suffix}</span> : null}
</Fragment>
<Match match={match} />
))}
</Text>
@@ -171,11 +167,7 @@ function SearchResult(props: SearchResultProps) {
}}
>
{match.map((match) => (
<Fragment key={match.id}>
<span>{match.prefix}</span>
<span className="match">{match.match}</span>
{match.suffix ? <span>{match.suffix}</span> : null}
</Fragment>
<Match match={match} />
))}
</Text>
}
@@ -193,6 +185,16 @@ function SearchResult(props: SearchResultProps) {
export default React.memo(SearchResult);
function Match({ match }: { match: TMatch }) {
return (
<Fragment key={match.id}>
<span>{match.prefix}</span>
<span className="match">{match.match}</span>
{match.suffix ? <span>{match.suffix}</span> : null}
</Fragment>
);
}
async function menuItems(
item: HighlightedResult,
ids?: string[]

View File

@@ -141,6 +141,23 @@ test("search reminders", () =>
}));
describe("notesWithHighlighting", () => {
test("search notes with nbsp in should decode html entities", () =>
noteTest({
title: "(with nbsp)",
content: {
type: "tiptap",
data: "<p>hello&nbsp;i&nbsp;am&nbsp;a&nbsp;note</p>"
}
}).then(async ({ db }) => {
const filtered = await db.lookup.notesWithHighlighting(
"hello",
db.notes.all
);
const item = await filtered.item(0);
expect(item.item).toBeDefined();
expect(item.item.content[0][0].suffix.includes("&nbsp;")).toBe(false);
}));
test("search notes with parentheses in query should load the item", () =>
noteTest({
title: "(with parantheses)"

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { match } from "fuzzyjs";
import { decodeHTML } from "entities";
import Database from "./index.js";
import {
HighlightedResult,
@@ -815,7 +816,7 @@ export function splitHighlightedMatch(text: string): Match[][] {
}
matches.push({
match,
match: match,
prefix: prefix.replace(/\s{2,}/gm, " ").trimStart(),
suffix: suffix || "",
id: matchId || undefined
@@ -1053,7 +1054,7 @@ function highlightHtmlContent(html: string, queries: string[]): string {
}
},
{
decodeEntities: false,
decodeEntities: true,
xmlMode: false
}
);

View File

@@ -225,7 +225,7 @@ export function extractMatchingBlocks(html: string, matchTagName: string) {
},
{
lowerCaseTags: false,
decodeEntities: false
decodeEntities: true
}
);
parser.end(html);