2018-08-06 22:29:03 +02:00
import server from "./server.js" ;
2022-12-01 13:07:23 +01:00
import appContext from "../components/app_context.js" ;
2025-01-09 18:07:02 +02:00
import noteCreateService from "./note_create.js" ;
2021-04-16 23:01:56 +02:00
import froca from "./froca.js" ;
2024-12-14 01:48:56 +02:00
import { t } from "./i18n.js" ;
2025-07-27 15:27:13 +03:00
import commandRegistry from "./command_registry.js" ;
2025-05-10 01:52:42 +03:00
import type { MentionFeedObjectItem } from "@triliumnext/ckeditor5" ;
2018-08-16 21:02:42 +02:00
2022-08-09 21:49:37 +02:00
// this key needs to have this value, so it's hit by the tooltip
2020-05-16 22:11:09 +02:00
const SELECTED_NOTE_PATH_KEY = "data-note-path" ;
2018-11-14 00:05:09 +01:00
2021-01-08 21:44:43 +01:00
const SELECTED_EXTERNAL_LINK_KEY = "data-external-link" ;
2025-04-01 18:47:07 +08:00
// To prevent search lag when there are a large number of notes, set a delay based on the number of notes to avoid jitter.
2025-04-01 21:07:15 +08:00
const notesCount = await server . get < number > ( ` autocomplete/notesCount ` ) ;
2025-04-01 18:47:07 +08:00
let debounceTimeoutId : ReturnType < typeof setTimeout > ;
function getSearchDelay ( notesCount : number ) : number {
2025-04-01 22:07:59 +03:00
const maxNotes = 20000 ;
const maxDelay = 1000 ;
2025-04-01 18:47:07 +08:00
const delay = Math . min ( maxDelay , ( notesCount / maxNotes ) * maxDelay ) ;
return delay ;
}
let searchDelay = getSearchDelay ( notesCount ) ;
2025-04-17 20:49:45 +03:00
// TODO: Deduplicate with server.
2024-12-21 14:34:16 +02:00
export interface Suggestion {
noteTitle? : string ;
externalLink? : string ;
notePathTitle? : string ;
notePath? : string ;
highlightedNotePathTitle? : string ;
2025-07-27 15:27:13 +03:00
action? : string | "create-note" | "search-notes" | "external-link" | "command" ;
2024-12-21 14:34:16 +02:00
parentNoteId? : string ;
2025-04-17 20:49:45 +03:00
icon? : string ;
2025-07-27 15:27:13 +03:00
commandId? : string ;
commandDescription? : string ;
commandShortcut? : string ;
2024-12-21 14:34:16 +02:00
}
2025-08-04 18:54:17 +03:00
export interface Options {
2025-08-10 13:02:17 +03:00
container? : HTMLElement | null ;
2024-12-21 14:34:16 +02:00
fastSearch? : boolean ;
allowCreatingNotes? : boolean ;
allowJumpToSearchNotes? : boolean ;
allowExternalLinks? : boolean ;
2025-07-17 21:07:44 +03:00
/** If set, hides the right-side button corresponding to go to selected note. */
2024-12-21 14:34:16 +02:00
hideGoToSelectedNoteButton? : boolean ;
2025-07-17 21:07:44 +03:00
/** If set, hides all right-side buttons in the autocomplete dropdown */
hideAllButtons? : boolean ;
2025-07-27 15:27:13 +03:00
/** If set, enables command palette mode */
isCommandPalette? : boolean ;
2024-12-21 14:34:16 +02:00
}
async function autocompleteSourceForCKEditor ( queryText : string ) {
2025-05-10 01:52:42 +03:00
return await new Promise < MentionFeedObjectItem [ ] > ( ( res , rej ) = > {
2025-01-09 18:07:02 +02:00
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
}
) ;
2020-09-21 22:08:54 +02:00
} ) ;
}
2024-12-21 14:34:16 +02:00
async function autocompleteSource ( term : string , cb : ( rows : Suggestion [ ] ) = > void , options : Options = { } ) {
2025-07-27 15:27:13 +03:00
// Check if we're in command mode
if ( options . isCommandPalette && term . startsWith ( ">" ) ) {
const commandQuery = term . substring ( 1 ) . trim ( ) ;
2025-08-04 18:54:17 +03:00
2025-07-27 21:39:55 +03:00
// Get commands (all if no query, filtered if query provided)
2025-08-04 18:54:17 +03:00
const commands = commandQuery . length === 0
2025-07-27 21:39:55 +03:00
? commandRegistry . getAllCommands ( )
: commandRegistry . searchCommands ( commandQuery ) ;
2025-08-04 18:54:17 +03:00
2025-07-27 21:39:55 +03:00
// Convert commands to suggestions
const commandSuggestions : Suggestion [ ] = commands . map ( cmd = > ( {
2025-07-27 15:27:13 +03:00
action : "command" ,
commandId : cmd.id ,
noteTitle : cmd.name ,
2025-07-27 21:36:42 +03:00
notePathTitle : ` > ${ cmd . name } ` ,
2025-07-27 15:27:13 +03:00
highlightedNotePathTitle : cmd.name ,
commandDescription : cmd.description ,
commandShortcut : cmd.shortcut ,
icon : cmd.icon
} ) ) ;
2025-08-04 18:54:17 +03:00
2025-07-27 15:27:13 +03:00
cb ( commandSuggestions ) ;
return ;
}
2024-11-27 09:51:34 +08:00
const fastSearch = options . fastSearch === false ? false : true ;
2024-11-26 16:20:38 +08:00
if ( fastSearch === false ) {
2025-01-09 18:07:02 +02:00
if ( term . trim ( ) . length === 0 ) {
2024-11-27 09:51:34 +08:00
return ;
}
2025-01-09 18:07:02 +02:00
cb ( [
{
2024-11-26 16:20:38 +08:00
noteTitle : term ,
2024-12-14 01:48:56 +02:00
highlightedNotePathTitle : t ( "quick-search.searching" )
2025-01-09 18:07:02 +02:00
}
] ) ;
2024-11-26 16:20:38 +08:00
}
2024-12-22 21:59:08 +02:00
2021-05-22 12:35:41 +02:00
const activeNoteId = appContext . tabManager . getActiveContextNoteId ( ) ;
2025-03-29 22:22:51 +02:00
const length = term . trim ( ) . length ;
2020-09-21 00:07:46 +02:00
2025-04-01 18:47:07 +08:00
let results = await server . get < Suggestion [ ] > ( ` autocomplete?query= ${ encodeURIComponent ( term ) } &activeNoteId= ${ activeNoteId } &fastSearch= ${ fastSearch } ` ) ;
options . fastSearch = true ;
2025-03-29 22:22:51 +02:00
if ( length >= 1 && options . allowCreatingNotes ) {
2020-09-21 00:07:46 +02:00
results = [
{
2025-01-09 18:07:02 +02:00
action : "create-note" ,
2020-09-21 00:07:46 +02:00
noteTitle : term ,
2025-01-09 18:07:02 +02:00
parentNoteId : activeNoteId || "root" ,
2024-12-14 01:48:56 +02:00
highlightedNotePathTitle : t ( "note_autocomplete.create-note" , { term } )
2024-12-21 14:34:16 +02:00
} as Suggestion
2020-09-21 00:07:46 +02:00
] . concat ( results ) ;
}
2018-08-16 21:02:42 +02:00
2025-03-29 22:22:51 +02:00
if ( length >= 1 && options . allowJumpToSearchNotes ) {
2024-11-20 14:22:39 +08:00
results = results . concat ( [
{
2025-01-09 18:07:02 +02:00
action : "search-notes" ,
2024-11-20 14:22:39 +08:00
noteTitle : term ,
2024-12-14 01:48:56 +02:00
highlightedNotePathTitle : ` ${ t ( "note_autocomplete.search-for" , { term } )} <kbd style='color: var(--muted-text-color); background-color: transparent; float: right;'>Ctrl+Enter</kbd> `
2024-11-20 14:22:39 +08:00
}
] ) ;
}
2021-01-08 21:44:43 +01:00
if ( term . match ( /^[a-z]+:\/\/.+/i ) && options . allowExternalLinks ) {
results = [
{
2025-01-09 18:07:02 +02:00
action : "external-link" ,
2021-01-08 21:44:43 +01:00
externalLink : term ,
2024-12-14 01:48:56 +02:00
highlightedNotePathTitle : t ( "note_autocomplete.insert-external-link" , { term } )
2024-12-21 14:34:16 +02:00
} as Suggestion
2021-01-08 21:44:43 +01:00
] . concat ( results ) ;
}
2020-09-21 00:07:46 +02:00
cb ( results ) ;
2018-08-16 21:02:42 +02:00
}
2018-08-06 22:29:03 +02:00
2024-12-21 14:34:16 +02:00
function clearText ( $el : JQuery < HTMLElement > ) {
2025-04-01 18:47:07 +08:00
searchDelay = 0 ;
2020-05-16 22:11:09 +02:00
$el . setSelectedNotePath ( "" ) ;
2025-01-09 18:07:02 +02:00
$el . autocomplete ( "val" , "" ) . trigger ( "change" ) ;
2018-11-13 23:16:26 +01:00
}
2024-12-21 14:34:16 +02:00
function setText ( $el : JQuery < HTMLElement > , text : string ) {
2021-01-05 14:22:11 +01:00
$el . setSelectedNotePath ( "" ) ;
2025-01-09 18:07:02 +02:00
$el . autocomplete ( "val" , text . trim ( ) ) . autocomplete ( "open" ) ;
2021-01-05 14:22:11 +01:00
}
2025-01-09 18:07:02 +02:00
function showRecentNotes ( $el : JQuery < HTMLElement > ) {
2025-04-01 18:47:07 +08:00
searchDelay = 0 ;
2020-05-16 22:11:09 +02:00
$el . setSelectedNotePath ( "" ) ;
2018-11-07 09:51:14 +01:00
$el . autocomplete ( "val" , "" ) ;
2025-01-09 18:07:02 +02:00
$el . autocomplete ( "open" ) ;
$el . trigger ( "focus" ) ;
2018-11-07 09:51:14 +01:00
}
2025-07-27 15:42:44 +03:00
function showAllCommands ( $el : JQuery < HTMLElement > ) {
searchDelay = 0 ;
$el . setSelectedNotePath ( "" ) ;
$el . autocomplete ( "val" , ">" ) . autocomplete ( "open" ) ;
}
2025-01-09 18:07:02 +02:00
function fullTextSearch ( $el : JQuery < HTMLElement > , options : Options ) {
const searchString = $el . autocomplete ( "val" ) as unknown as string ;
2024-12-21 14:34:16 +02:00
if ( options . fastSearch === false || searchString ? . trim ( ) . length === 0 ) {
2024-11-27 09:51:34 +08:00
return ;
2024-12-22 21:59:08 +02:00
}
2025-01-09 18:07:02 +02:00
$el . trigger ( "focus" ) ;
2024-11-27 09:51:34 +08:00
options . fastSearch = false ;
2025-01-09 18:07:02 +02:00
$el . autocomplete ( "val" , "" ) ;
2024-11-27 09:51:34 +08:00
$el . setSelectedNotePath ( "" ) ;
2025-04-01 18:47:07 +08:00
searchDelay = 0 ;
2025-01-09 18:07:02 +02:00
$el . autocomplete ( "val" , searchString ) ;
2024-11-23 20:51:51 +08:00
}
2024-12-21 14:39:36 +02:00
function initNoteAutocomplete ( $el : JQuery < HTMLElement > , options? : Options ) {
2025-01-04 23:43:15 +02:00
if ( $el . hasClass ( "note-autocomplete-input" ) ) {
2020-10-02 21:44:21 +02:00
// clear any event listener added in previous invocation of this function
2025-01-09 18:07:02 +02:00
$el . off ( "autocomplete:noteselected" ) ;
2020-10-02 21:44:21 +02:00
2019-05-22 20:53:59 +02:00
return $el ;
}
2018-11-13 23:16:26 +01:00
2019-05-22 20:53:59 +02:00
options = options || { } ;
2018-11-13 23:16:26 +01:00
2025-04-01 18:47:07 +08:00
// Used to track whether the user is performing character composition with an input method (such as Chinese Pinyin, Japanese, Korean, etc.) and to avoid triggering a search during the composition process.
let isComposingInput = false ;
$el . on ( "compositionstart" , ( ) = > {
2025-04-01 22:07:59 +03:00
isComposingInput = true ;
2025-04-01 18:47:07 +08:00
} ) ;
$el . on ( "compositionend" , ( ) = > {
2025-04-01 22:07:59 +03:00
isComposingInput = false ;
2025-04-02 15:19:19 +08:00
const searchString = $el . autocomplete ( "val" ) as unknown as string ;
$el . autocomplete ( "val" , "" ) ;
$el . autocomplete ( "val" , searchString ) ;
2025-04-01 18:47:07 +08:00
} ) ;
2019-05-22 20:53:59 +02:00
$el . addClass ( "note-autocomplete-input" ) ;
2018-11-14 11:17:20 +01:00
2025-02-13 22:09:08 +02:00
const $clearTextButton = $ ( "<a>" ) . addClass ( "input-group-text input-clearer-button bx bxs-tag-x" ) . prop ( "title" , t ( "note_autocomplete.clear-text-field" ) ) ;
2018-11-14 11:28:52 +01:00
2025-02-13 22:09:08 +02:00
const $showRecentNotesButton = $ ( "<a>" ) . addClass ( "input-group-text show-recent-notes-button bx bx-time" ) . prop ( "title" , t ( "note_autocomplete.show-recent-notes" ) ) ;
2018-11-14 11:28:52 +01:00
2025-02-13 22:09:08 +02:00
const $fullTextSearchButton = $ ( "<a>" )
2024-11-23 20:51:51 +08:00
. addClass ( "input-group-text full-text-search-button bx bx-search" )
2024-12-22 21:59:08 +02:00
. prop ( "title" , ` ${ t ( "note_autocomplete.full-text-search" ) } (Shift+Enter) ` ) ;
2024-11-23 20:51:51 +08:00
2025-01-09 18:07:02 +02:00
const $goToSelectedNoteButton = $ ( "<a>" ) . addClass ( "input-group-text go-to-selected-note-button bx bx-arrow-to-right" ) ;
2018-08-06 22:29:03 +02:00
2025-07-17 21:07:44 +03:00
if ( ! options . hideAllButtons ) {
$el . after ( $clearTextButton ) . after ( $showRecentNotesButton ) . after ( $fullTextSearchButton ) ;
}
2018-08-06 22:29:03 +02:00
2025-07-17 21:07:44 +03:00
if ( ! options . hideGoToSelectedNoteButton && ! options . hideAllButtons ) {
2024-09-03 17:08:07 +02:00
$el . after ( $goToSelectedNoteButton ) ;
2019-05-22 20:53:59 +02:00
}
2019-01-10 21:04:06 +01:00
2025-01-09 18:07:02 +02:00
$clearTextButton . on ( "click" , ( ) = > clearText ( $el ) ) ;
2019-05-22 20:53:59 +02:00
2025-01-09 18:07:02 +02:00
$showRecentNotesButton . on ( "click" , ( e ) = > {
2019-05-22 20:53:59 +02:00
showRecentNotes ( $el ) ;
// this will cause the click not give focus to the "show recent notes" button
2023-06-23 00:26:47 +08:00
// this is important because otherwise input will lose focus immediately and not show the results
2019-05-22 20:53:59 +02:00
return false ;
} ) ;
2024-11-17 12:18:05 +08:00
2025-01-09 18:07:02 +02:00
$fullTextSearchButton . on ( "click" , ( e ) = > {
2024-11-23 20:51:51 +08:00
fullTextSearch ( $el , options ) ;
2024-11-26 15:41:18 +08:00
return false ;
2024-11-23 20:51:51 +08:00
} ) ;
2024-12-21 14:34:16 +02:00
let autocompleteOptions : AutoCompleteConfig = { } ;
2023-08-26 17:03:09 +03:00
if ( options . container ) {
autocompleteOptions . dropdownMenuContainer = options . container ;
2025-01-09 18:07:02 +02:00
autocompleteOptions . debug = true ; // don't close on blur
2023-08-26 17:03:09 +03:00
}
2024-11-24 13:11:57 +08:00
if ( options . allowJumpToSearchNotes ) {
2025-01-09 18:07:02 +02:00
$el . on ( "keydown" , ( event ) = > {
if ( event . ctrlKey && event . key === "Enter" ) {
2024-11-20 14:22:39 +08:00
// Prevent Ctrl + Enter from triggering autoComplete.
event . stopImmediatePropagation ( ) ;
event . preventDefault ( ) ;
2025-01-09 18:07:02 +02:00
$el . trigger ( "autocomplete:selected" , { action : "search-notes" , noteTitle : $el.autocomplete ( "val" ) } ) ;
2024-11-20 14:22:39 +08:00
}
} ) ;
}
2025-01-09 18:07:02 +02:00
$el . on ( "keydown" , async ( event ) = > {
if ( event . shiftKey && event . key === "Enter" ) {
2024-11-23 20:51:51 +08:00
// Prevent Enter from triggering autoComplete.
event . stopImmediatePropagation ( ) ;
event . preventDefault ( ) ;
2025-01-09 18:07:02 +02:00
fullTextSearch ( $el , options ) ;
2024-11-23 20:51:51 +08:00
}
} ) ;
2024-12-22 21:59:08 +02:00
2025-01-09 18:07:02 +02:00
$el . autocomplete (
2019-05-22 20:53:59 +02:00
{
2025-01-09 18:07:02 +02:00
. . . autocompleteOptions ,
appendTo : document.querySelector ( "body" ) ,
hint : false ,
autoselect : true ,
// openOnFocus has to be false, otherwise re-focus (after return from note type chooser dialog) forces
// re-querying of the autocomplete source which then changes the currently selected suggestion
openOnFocus : false ,
minLength : 0 ,
tabAutocomplete : false
} ,
[
{
2025-04-01 18:47:07 +08:00
source : ( term , cb ) = > {
clearTimeout ( debounceTimeoutId ) ;
debounceTimeoutId = setTimeout ( ( ) = > {
if ( isComposingInput ) {
return ;
}
autocompleteSource ( term , cb , options ) ;
2025-04-01 22:07:59 +03:00
} , searchDelay ) ;
2025-04-01 18:47:07 +08:00
if ( searchDelay === 0 ) {
searchDelay = getSearchDelay ( notesCount ) ;
}
} ,
2025-01-09 18:07:02 +02:00
displayKey : "notePathTitle" ,
templates : {
2025-07-27 15:27:13 +03:00
suggestion : ( suggestion ) = > {
if ( suggestion . action === "command" ) {
let html = ` <div class="command-suggestion"> ` ;
2025-07-27 15:44:47 +03:00
html += ` <span class="command-icon ${ suggestion . icon || "bx bx-terminal" } "></span> ` ;
2025-07-27 18:11:43 +03:00
html += ` <div class="command-content"> ` ;
html += ` <div class="command-name"> ${ suggestion . highlightedNotePathTitle } </div> ` ;
2025-07-27 15:27:13 +03:00
if ( suggestion . commandDescription ) {
2025-07-27 15:44:47 +03:00
html += ` <div class="command-description"> ${ suggestion . commandDescription } </div> ` ;
2025-07-27 15:27:13 +03:00
}
2025-07-27 18:11:43 +03:00
html += ` </div> ` ;
if ( suggestion . commandShortcut ) {
html += ` <kbd class="command-shortcut"> ${ suggestion . commandShortcut } </kbd> ` ;
}
2025-07-27 15:27:13 +03:00
html += '</div>' ;
return html ;
}
return ` <span class=" ${ suggestion . icon ? ? "bx bx-note" } "></span> ${ suggestion . highlightedNotePathTitle } ` ;
}
2025-01-09 18:07:02 +02:00
} ,
// we can't cache identical searches because notes can be created / renamed, new recent notes can be added
cache : false
}
]
) ;
2018-11-07 09:51:14 +01:00
2024-12-21 14:34:16 +02:00
// TODO: Types fail due to "autocomplete:selected" not being registered in type definitions.
2025-01-09 18:07:02 +02:00
( $el as any ) . on ( "autocomplete:selected" , async ( event : Event , suggestion : Suggestion ) = > {
2025-07-27 15:27:13 +03:00
if ( suggestion . action === "command" ) {
$el . autocomplete ( "close" ) ;
$el . trigger ( "autocomplete:commandselected" , [ suggestion ] ) ;
return ;
}
2025-01-09 18:07:02 +02:00
if ( suggestion . action === "external-link" ) {
2021-01-08 21:44:43 +01:00
$el . setSelectedNotePath ( null ) ;
$el . setSelectedExternalLink ( suggestion . externalLink ) ;
$el . autocomplete ( "val" , suggestion . externalLink ) ;
$el . autocomplete ( "close" ) ;
2025-01-09 18:07:02 +02:00
$el . trigger ( "autocomplete:externallinkselected" , [ suggestion ] ) ;
2021-01-08 21:44:43 +01:00
return ;
}
2025-01-09 18:07:02 +02:00
if ( suggestion . action === "create-note" ) {
2025-06-17 20:41:23 +08:00
const { success , noteType , templateNoteId , notePath } = await noteCreateService . chooseNoteType ( ) ;
2022-06-02 14:16:49 +02:00
if ( ! success ) {
return ;
}
2025-06-17 20:41:23 +08:00
const { note } = await noteCreateService . createNote ( notePath || suggestion . parentNoteId , {
2020-09-21 00:07:46 +02:00
title : suggestion.noteTitle ,
2022-06-02 14:16:49 +02:00
activate : false ,
type : noteType ,
templateNoteId : templateNoteId
2020-09-21 00:07:46 +02:00
} ) ;
2023-04-15 00:06:13 +02:00
const hoistedNoteId = appContext . tabManager . getActiveContext ( ) ? . hoistedNoteId ;
2024-12-21 14:34:16 +02:00
suggestion . notePath = note ? . getBestNotePathString ( hoistedNoteId ) ;
2020-09-21 00:07:46 +02:00
}
2025-01-09 18:07:02 +02:00
if ( suggestion . action === "search-notes" ) {
2024-11-20 14:22:39 +08:00
const searchString = suggestion . noteTitle ;
2025-01-09 18:07:02 +02:00
appContext . triggerCommand ( "searchNotes" , { searchString } ) ;
2024-11-20 14:22:39 +08:00
return ;
}
2024-12-22 21:59:08 +02:00
2020-09-03 17:38:11 +02:00
$el . setSelectedNotePath ( suggestion . notePath ) ;
2021-01-08 21:44:43 +01:00
$el . setSelectedExternalLink ( null ) ;
2020-09-03 17:38:11 +02:00
$el . autocomplete ( "val" , suggestion . noteTitle ) ;
2020-09-21 00:07:46 +02:00
$el . autocomplete ( "close" ) ;
2025-01-09 18:07:02 +02:00
$el . trigger ( "autocomplete:noteselected" , [ suggestion ] ) ;
2020-09-03 17:38:11 +02:00
} ) ;
2025-01-09 18:07:02 +02:00
$el . on ( "autocomplete:closed" , ( ) = > {
2024-12-21 14:34:16 +02:00
if ( ! String ( $el . val ( ) ) ? . trim ( ) ) {
2019-05-22 20:53:59 +02:00
clearText ( $el ) ;
}
} ) ;
2018-11-07 09:51:14 +01:00
2025-01-09 18:07:02 +02:00
$el . on ( "autocomplete:opened" , ( ) = > {
2020-08-11 22:52:17 +02:00
if ( $el . attr ( "readonly" ) ) {
2025-01-09 18:07:02 +02:00
$el . autocomplete ( "close" ) ;
2020-08-11 22:52:17 +02:00
}
} ) ;
2020-10-02 21:44:21 +02:00
// clear any event listener added in previous invocation of this function
2025-01-09 18:07:02 +02:00
$el . off ( "autocomplete:noteselected" ) ;
2020-10-02 21:44:21 +02:00
2018-11-07 09:51:14 +01:00
return $el ;
2018-08-06 22:29:03 +02:00
}
2018-12-24 10:10:36 +01:00
function init() {
2020-05-16 22:11:09 +02:00
$ . fn . getSelectedNotePath = function ( ) {
2024-12-21 14:34:16 +02:00
if ( ! String ( $ ( this ) . val ( ) ) ? . trim ( ) ) {
2018-12-24 10:10:36 +01:00
return "" ;
} else {
2020-05-16 22:11:09 +02:00
return $ ( this ) . attr ( SELECTED_NOTE_PATH_KEY ) ;
2018-12-24 10:10:36 +01:00
}
} ;
2018-11-12 23:34:22 +01:00
2020-08-12 23:39:05 +02:00
$ . fn . getSelectedNoteId = function ( ) {
2024-12-21 14:34:16 +02:00
const $el = $ ( this as unknown as HTMLElement ) ;
const notePath = $el . getSelectedNotePath ( ) ;
2023-01-13 10:09:41 +01:00
if ( ! notePath ) {
return null ;
}
2025-01-09 18:07:02 +02:00
const chunks = notePath . split ( "/" ) ;
2020-08-12 23:39:05 +02:00
return chunks . length >= 1 ? chunks [ chunks . length - 1 ] : null ;
2025-01-09 18:07:02 +02:00
} ;
2020-08-12 23:39:05 +02:00
2020-05-16 22:11:09 +02:00
$ . fn . setSelectedNotePath = function ( notePath ) {
notePath = notePath || "" ;
$ ( this ) . attr ( SELECTED_NOTE_PATH_KEY , notePath ) ;
2025-01-09 18:07:02 +02:00
$ ( this ) . closest ( ".input-group" ) . find ( ".go-to-selected-note-button" ) . toggleClass ( "disabled" , ! notePath . trim ( ) ) . attr ( "href" , ` # ${ notePath } ` ) ; // we also set href here so tooltip can be displayed
2018-12-24 10:10:36 +01:00
} ;
2021-01-08 21:44:43 +01:00
$ . fn . getSelectedExternalLink = function ( ) {
2024-12-21 14:34:16 +02:00
if ( ! String ( $ ( this ) . val ( ) ) ? . trim ( ) ) {
2021-01-08 21:44:43 +01:00
return "" ;
} else {
return $ ( this ) . attr ( SELECTED_EXTERNAL_LINK_KEY ) ;
}
} ;
2025-03-04 00:33:09 +01:00
$ . fn . setSelectedExternalLink = function ( externalLink : string | null ) {
$ ( this ) . attr ( SELECTED_EXTERNAL_LINK_KEY , externalLink ) ;
$ ( this ) . closest ( ".input-group" ) . find ( ".go-to-selected-note-button" ) . toggleClass ( "disabled" , true ) ;
2025-01-09 18:07:02 +02:00
} ;
2021-01-19 22:10:24 +01:00
$ . fn . setNote = async function ( noteId ) {
2021-04-16 22:57:37 +02:00
const note = noteId ? await froca . getNote ( noteId , true ) : null ;
2021-01-19 22:10:24 +01:00
$ ( this )
. val ( note ? note . title : "" )
. setSelectedNotePath ( noteId ) ;
2025-01-09 18:07:02 +02:00
} ;
2018-12-24 10:10:36 +01:00
}
2018-08-06 22:29:03 +02:00
2025-08-05 21:06:12 +03:00
/ * *
* Convenience function which triggers the display of recent notes in the autocomplete input and focuses it .
*
* @param inputElement - The input element to trigger recent notes on .
* /
export function triggerRecentNotes ( inputElement : HTMLInputElement | null | undefined ) {
if ( ! inputElement ) {
return ;
}
const $el = $ ( inputElement ) ;
showRecentNotes ( $el ) ;
$el . trigger ( "focus" ) . trigger ( "select" ) ;
}
2018-08-06 22:29:03 +02:00
export default {
2020-09-21 22:08:54 +02:00
autocompleteSourceForCKEditor ,
2018-08-16 21:02:42 +02:00
initNoteAutocomplete ,
2018-12-24 10:10:36 +01:00
showRecentNotes ,
2025-07-27 15:42:44 +03:00
showAllCommands ,
2021-01-05 14:22:11 +01:00
setText ,
2018-12-24 10:10:36 +01:00
init
2025-01-09 18:07:02 +02:00
} ;