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..8db84e448 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() || ""; } @@ -235,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; diff --git a/apps/client/src/widgets/find_in_html.ts b/apps/client/src/widgets/find_in_html.ts index bc63d78ce..e4d47e623 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,16 @@ export default class FindInHtml { caseSensitive: matchCase, done: async () => { this.$results = $content.find(`.${FIND_RESULT_CSS_CLASSNAME}`); - this.currentIndex = 0; + 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: Math.min(1, this.$results.length) + currentFound: this.$results.length > 0 ? this.currentIndex + 1 : 0 }); } }); @@ -71,27 +74,17 @@ 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(); } } 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); } } } diff --git a/apps/client/src/widgets/find_in_text.ts b/apps/client/src/widgets/find_in_text.ts index 248c0bc0b..959fcdf34 100644 --- a/apps/client/src/widgets/find_in_text.ts +++ b/apps/client/src/widgets/find_in_text.ts @@ -55,15 +55,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 && 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; } }