import server from "./server.js"; import appContext from "../components/app_context.js"; import utils from "./utils.js"; import noteCreateService from "./note_create.js"; import froca from "./froca.js"; import { t } from "./i18n.js"; // this key needs to have this value, so it's hit by the tooltip const SELECTED_NOTE_PATH_KEY = "data-note-path"; const SELECTED_EXTERNAL_LINK_KEY = "data-external-link"; export interface Suggestion { noteTitle?: string; externalLink?: string; notePathTitle?: string; notePath?: string; highlightedNotePathTitle?: string; action?: string | "create-note" | "search-notes" | "external-link"; parentNoteId?: string; } interface Options { container?: HTMLElement; fastSearch?: boolean; allowCreatingNotes?: boolean; allowJumpToSearchNotes?: boolean; allowExternalLinks?: boolean; hideGoToSelectedNoteButton?: boolean; } async function autocompleteSourceForCKEditor(queryText: string) { return await new Promise((res, rej) => { autocompleteSource( queryText, (rows) => { res( rows.map((row) => { return { action: row.action, noteTitle: row.noteTitle, id: `@${row.notePathTitle}`, name: row.notePathTitle || "", link: `#${row.notePath}`, notePath: row.notePath, highlightedNotePathTitle: row.highlightedNotePathTitle }; }) ); }, { allowCreatingNotes: true } ); }); } async function autocompleteSource(term: string, cb: (rows: Suggestion[]) => void, options: Options = {}) { const fastSearch = options.fastSearch === false ? false : true; if (fastSearch === false) { if (term.trim().length === 0) { return; } cb([ { noteTitle: term, highlightedNotePathTitle: t("quick-search.searching") } ]); } const activeNoteId = appContext.tabManager.getActiveContextNoteId(); let results: Suggestion[] = await server.get(`autocomplete?query=${encodeURIComponent(term)}&activeNoteId=${activeNoteId}&fastSearch=${fastSearch}`); if (term.trim().length >= 1 && options.allowCreatingNotes) { results = [ { action: "create-note", noteTitle: term, parentNoteId: activeNoteId || "root", highlightedNotePathTitle: t("note_autocomplete.create-note", { term }) } as Suggestion ].concat(results); } if (term.trim().length >= 1 && options.allowJumpToSearchNotes) { results = results.concat([ { action: "search-notes", noteTitle: term, highlightedNotePathTitle: `${t("note_autocomplete.search-for", { term })} Ctrl+Enter` } ]); } if (term.match(/^[a-z]+:\/\/.+/i) && options.allowExternalLinks) { results = [ { action: "external-link", externalLink: term, highlightedNotePathTitle: t("note_autocomplete.insert-external-link", { term }) } as Suggestion ].concat(results); } cb(results); } function clearText($el: JQuery) { $el.setSelectedNotePath(""); $el.autocomplete("val", "").trigger("change"); } function setText($el: JQuery, text: string) { $el.setSelectedNotePath(""); $el.autocomplete("val", text.trim()).autocomplete("open"); } function showRecentNotes($el: JQuery) { $el.setSelectedNotePath(""); $el.autocomplete("val", ""); $el.autocomplete("open"); $el.trigger("focus"); } function fullTextSearch($el: JQuery, options: Options) { const searchString = $el.autocomplete("val") as unknown as string; if (options.fastSearch === false || searchString?.trim().length === 0) { return; } $el.trigger("focus"); options.fastSearch = false; $el.autocomplete("val", ""); $el.setSelectedNotePath(""); $el.autocomplete("val", searchString); // Set a delay to avoid resetting to true before full text search (await server.get) is called. setTimeout(() => { options.fastSearch = true; }, 100); } function initNoteAutocomplete($el: JQuery, options?: Options) { if ($el.hasClass("note-autocomplete-input")) { // clear any event listener added in previous invocation of this function $el.off("autocomplete:noteselected"); return $el; } options = options || {}; $el.addClass("note-autocomplete-input"); const $clearTextButton = $("