From 858814356a17b3aee17da34aa624bafe97101578 Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Thu, 8 May 2025 16:25:44 +0800 Subject: [PATCH 1/4] Make the find function for read-only code scroll correctly. --- apps/client/src/stylesheets/style.css | 8 +++++++ apps/client/src/widgets/find.ts | 19 +++++++++------ apps/client/src/widgets/find_in_html.ts | 32 +++++++++++++------------ 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css index 11f446e56..544b6581b 100644 --- a/apps/client/src/stylesheets/style.css +++ b/apps/client/src/stylesheets/style.css @@ -438,6 +438,14 @@ body .CodeMirror { background-color: #eeeeee; } +.cm-matchhighlight.ck-find-result{ + background: var(--ck-color-highlight-background); +} + +.cm-matchhighlight.ck-find-result_selected { + background-color: #ff9633; +} + .CodeMirror pre.CodeMirror-placeholder { color: #999 !important; } diff --git a/apps/client/src/widgets/find.ts b/apps/client/src/widgets/find.ts index d4a6c9b6a..aa4966a16 100644 --- a/apps/client/src/widgets/find.ts +++ b/apps/client/src/widgets/find.ts @@ -143,9 +143,9 @@ export default class FindWidget extends NoteContextAwareWidget { this.$currentFound = this.$widget.find(".find-widget-current-found"); this.$totalFound = this.$widget.find(".find-widget-total-found"); this.$caseSensitiveCheckbox = this.$widget.find(".find-widget-case-sensitive-checkbox"); - this.$caseSensitiveCheckbox.change(() => this.performFind()); + this.$caseSensitiveCheckbox.on("change", () => this.performFind()); this.$matchWordsCheckbox = this.$widget.find(".find-widget-match-words-checkbox"); - this.$matchWordsCheckbox.change(() => this.performFind()); + this.$matchWordsCheckbox.on("change", () => this.performFind()); this.$previousButton = this.$widget.find(".find-widget-previous-button"); this.$previousButton.on("click", () => this.findNext(-1)); this.$nextButton = this.$widget.find(".find-widget-next-button"); @@ -160,7 +160,7 @@ export default class FindWidget extends NoteContextAwareWidget { this.$replaceButton = this.$widget.find(".replace-widget-replace-button"); this.$replaceButton.on("click", () => this.replace()); - this.$input.keydown(async (e) => { + this.$input.on("keydown", async (e) => { if ((e.metaKey || e.ctrlKey) && (e.key === "F" || e.key === "f")) { // If ctrl+f is pressed when the findbox is shown, select the // whole input to find @@ -172,7 +172,7 @@ export default class FindWidget extends NoteContextAwareWidget { } }); - this.$widget.keydown(async (e) => { + this.$widget.on("keydown", async (e) => { if (e.key === "Escape") { await this.closeSearch(); } @@ -197,9 +197,14 @@ export default class FindWidget extends NoteContextAwareWidget { const isReadOnly = await this.noteContext?.isReadOnly(); let selectedText = ""; - if (this.note?.type === "code" && !isReadOnly && this.noteContext) { - const codeEditor = await this.noteContext.getCodeEditor(); - selectedText = codeEditor.getSelection(); + if (this.note?.type === "code" && this.noteContext) { + if (isReadOnly){ + const $content = await this.noteContext.getContentElement(); + selectedText = $content.find('.cm-matchhighlight').first().text(); + } else { + const codeEditor = await this.noteContext.getCodeEditor(); + selectedText = codeEditor.getSelection(); + } } else { selectedText = window.getSelection()?.toString() || ""; } diff --git a/apps/client/src/widgets/find_in_html.ts b/apps/client/src/widgets/find_in_html.ts index bc63d78ce..4885fda31 100644 --- a/apps/client/src/widgets/find_in_html.ts +++ b/apps/client/src/widgets/find_in_html.ts @@ -2,7 +2,6 @@ // uses for highlighting matches, use the same one on CodeMirror // for consistency import utils from "../services/utils.js"; -import appContext from "../components/app_context.js"; import type FindWidget from "./find.js"; import type { FindResult } from "./find.js"; @@ -39,12 +38,25 @@ export default class FindInHtml { caseSensitive: matchCase, done: async () => { this.$results = $content.find(`.${FIND_RESULT_CSS_CLASSNAME}`); - this.currentIndex = 0; + let closestIndex = 0; + let minTop = Infinity; + + this.$results.each((i, el) => { + const rect = el.getBoundingClientRect(); + const top = rect.top; + + if (top >= 0 && top < minTop) { + minTop = top; + closestIndex = i; + } + }); + + this.currentIndex = closestIndex; await this.jumpTo(); res({ totalFound: this.$results.length, - currentFound: Math.min(1, this.$results.length) + currentFound: this.$results.length > 0 ? closestIndex + 1 : 0 }); } }); @@ -78,20 +90,10 @@ export default class FindInHtml { async jumpTo() { if (this.$results?.length) { - const offsetTop = 100; const $current = this.$results.eq(this.currentIndex); this.$results.removeClass(FIND_RESULT_SELECTED_CSS_CLASSNAME); - - if ($current.length) { - $current.addClass(FIND_RESULT_SELECTED_CSS_CLASSNAME); - const position = $current.position().top - offsetTop; - - const $content = await this.parent.noteContext?.getContentElement(); - if ($content) { - const $contentWidget = appContext.getComponentByEl($content[0]); - $contentWidget.triggerCommand("scrollContainerTo", { position }); - } - } + $current[0].scrollIntoView(); + $current.addClass(FIND_RESULT_SELECTED_CSS_CLASSNAME); } } } From fc50252e97073cf02ba2f5c7f57f8b02a9adfc01 Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Fri, 9 May 2025 09:37:50 +0800 Subject: [PATCH 2/4] Close the find dialog when the note is temporarily readable --- apps/client/src/widgets/find.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/client/src/widgets/find.ts b/apps/client/src/widgets/find.ts index aa4966a16..8db84e448 100644 --- a/apps/client/src/widgets/find.ts +++ b/apps/client/src/widgets/find.ts @@ -240,6 +240,12 @@ export default class FindWidget extends NoteContextAwareWidget { } } + async readOnlyTemporarilyDisabledEvent({ noteContext }: EventData<"readOnlyTemporarilyDisabled">) { + if (this.isNoteContext(noteContext.ntxId)) { + await this.closeSearch(); + } + } + async getHandler() { if (this.note?.type === "render") { return this.htmlHandler; From 67e84d921fab932ef7bbac3c2367056364c81e76 Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Fri, 9 May 2025 10:00:12 +0800 Subject: [PATCH 3/4] Avoid reporting errors when performFind has not been called --- apps/client/src/widgets/find_in_html.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/find_in_html.ts b/apps/client/src/widgets/find_in_html.ts index 4885fda31..e8ae8b5ab 100644 --- a/apps/client/src/widgets/find_in_html.ts +++ b/apps/client/src/widgets/find_in_html.ts @@ -83,7 +83,7 @@ export default class FindInHtml { async findBoxClosed(totalFound: number, currentFound: number) { const $content = await this.parent?.noteContext?.getContentElement(); - if ($content) { + if (typeof $content?.unmark === 'function') { $content.unmark(); } } From aecde9656b1b5772bd5adf3764212f692091cc1e Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Fri, 9 May 2025 18:48:28 +0800 Subject: [PATCH 4/4] highlight the first visible find result in scrolling container. --- apps/client/src/widgets/find_in_html.ts | 21 ++++++------------- apps/client/src/widgets/find_in_text.ts | 27 +++++++++++++++++-------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/apps/client/src/widgets/find_in_html.ts b/apps/client/src/widgets/find_in_html.ts index e8ae8b5ab..e4d47e623 100644 --- a/apps/client/src/widgets/find_in_html.ts +++ b/apps/client/src/widgets/find_in_html.ts @@ -38,25 +38,16 @@ export default class FindInHtml { caseSensitive: matchCase, done: async () => { this.$results = $content.find(`.${FIND_RESULT_CSS_CLASSNAME}`); - let closestIndex = 0; - let minTop = Infinity; - - this.$results.each((i, el) => { - const rect = el.getBoundingClientRect(); - const top = rect.top; - - if (top >= 0 && top < minTop) { - minTop = top; - closestIndex = i; - } - }); - - this.currentIndex = closestIndex; + const scrollingContainer = $content[0].closest('.scrolling-container'); + const containerTop = scrollingContainer?.getBoundingClientRect().top ?? 0; + const closestIndex = this.$results.toArray().findIndex(el => el.getBoundingClientRect().top >= containerTop); + this.currentIndex = closestIndex >= 0 ? closestIndex : 0; + await this.jumpTo(); res({ totalFound: this.$results.length, - currentFound: this.$results.length > 0 ? closestIndex + 1 : 0 + currentFound: this.$results.length > 0 ? this.currentIndex + 1 : 0 }); } }); diff --git a/apps/client/src/widgets/find_in_text.ts b/apps/client/src/widgets/find_in_text.ts index b5fa3a02c..9ae0e2007 100644 --- a/apps/client/src/widgets/find_in_text.ts +++ b/apps/client/src/widgets/find_in_text.ts @@ -54,15 +54,26 @@ export default class FindInText { const options = { matchCase: matchCase, wholeWords: wholeWord }; findResult = textEditor.execute("find", searchTerm, options); totalFound = findResult.results.length; - // Find the result beyond the cursor - const cursorPos = model.document.selection.getLastPosition(); - for (let i = 0; i < findResult.results.length; ++i) { - const marker = findResult.results.get(i).marker; - const fromPos = marker.getStart(); - if (cursorPos && fromPos.compareWith(cursorPos) !== "before") { - currentFound = i; - break; + const selection = model.document.selection; + // If text is selected, highlight the corresponding result; + // otherwise, highlight the first visible result in the scrolling container. + if (!selection.isCollapsed) { + const cursorPos = selection.getFirstPosition(); + for (let i = 0; i < findResult.results.length; ++i) { + const marker = findResult.results.get(i).marker; + const fromPos = marker.getStart(); + if (cursorPos && fromPos.compareWith(cursorPos) !== "before") { + currentFound = i; + break; + } } + } else { + const editorEl = textEditor?.sourceElement; + const findResultElement = editorEl.querySelectorAll(".ck-find-result"); + const scrollingContainer = editorEl.closest('.scrolling-container'); + const containerTop = scrollingContainer?.getBoundingClientRect().top ?? 0; + const closestIndex = Array.from(findResultElement ?? []).findIndex((el) => el.getBoundingClientRect().top >= containerTop); + currentFound = closestIndex >= 0 ? closestIndex : 0; } }