| 
									
										
										
										
											2024-07-24 17:34:26 +08:00
										 |  |  | import { t } from "../../services/i18n.js"; | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  | import noteAutocompleteService from "../../services/note_autocomplete.js"; | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  | import utils from "../../services/utils.js"; | 
					
						
							| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  | import appContext from "../../components/app_context.js"; | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  | import BasicWidget from "../basic_widget.js"; | 
					
						
							| 
									
										
										
										
											2022-12-01 00:17:15 +01:00
										 |  |  | import shortcutService from "../../services/shortcuts.js"; | 
					
						
							| 
									
										
										
										
											2025-02-21 20:41:00 +01:00
										 |  |  | import { Modal } from "bootstrap"; | 
					
						
							| 
									
										
										
										
											2025-06-19 22:44:02 +03:00
										 |  |  | import { openDialog } from "../../services/dialog.js"; | 
					
						
							| 
									
										
										
										
											2025-07-27 15:27:13 +03:00
										 |  |  | import commandRegistry from "../../services/command_registry.js"; | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 23:24:21 +03:00
										 |  |  | const TPL = /*html*/`<div class="jump-to-note-dialog modal mx-auto" tabindex="-1" role="dialog">
 | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |     <div class="modal-dialog modal-lg" role="document"> | 
					
						
							|  |  |  |         <div class="modal-content"> | 
					
						
							|  |  |  |             <div class="modal-header"> | 
					
						
							| 
									
										
										
										
											2023-08-28 22:15:36 +03:00
										 |  |  |                 <div class="input-group"> | 
					
						
							| 
									
										
										
										
											2025-07-27 15:27:13 +03:00
										 |  |  |                     <input class="jump-to-note-autocomplete form-control" placeholder="${t("jump_to_note.search_placeholder", { defaultValue: "Search notes or type > for commands..." })}"> | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |                 </div> | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |                 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("jump_to_note.close")}"></button> | 
					
						
							| 
									
										
										
										
											2023-08-26 17:48:12 +03:00
										 |  |  |             </div> | 
					
						
							|  |  |  |             <div class="modal-body"> | 
					
						
							| 
									
										
										
										
											2023-08-26 17:19:44 +03:00
										 |  |  |                 <div class="algolia-autocomplete-container jump-to-note-results"></div> | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |             </div> | 
					
						
							|  |  |  |             <div class="modal-footer"> | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |                 <button class="show-in-full-text-button btn btn-sm">${t("jump_to_note.search_button")}</button> | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |             </div> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |     </div> | 
					
						
							|  |  |  | </div>`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const KEEP_LAST_SEARCH_FOR_X_SECONDS = 120; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default class JumpToNoteDialog extends BasicWidget { | 
					
						
							| 
									
										
										
										
											2025-03-19 23:06:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     private lastOpenedTs: number; | 
					
						
							|  |  |  |     private modal!: bootstrap.Modal; | 
					
						
							|  |  |  |     private $autoComplete!: JQuery<HTMLElement>; | 
					
						
							|  |  |  |     private $results!: JQuery<HTMLElement>; | 
					
						
							| 
									
										
										
										
											2025-07-27 18:15:54 +03:00
										 |  |  |     private $modalFooter!: JQuery<HTMLElement>; | 
					
						
							| 
									
										
										
										
											2025-07-27 15:58:29 +03:00
										 |  |  |     private isCommandMode: boolean = false; | 
					
						
							| 
									
										
										
										
											2025-03-19 23:06:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |     constructor() { | 
					
						
							|  |  |  |         super(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.lastOpenedTs = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     doRender() { | 
					
						
							|  |  |  |         this.$widget = $(TPL); | 
					
						
							| 
									
										
										
										
											2025-03-19 23:06:16 +02:00
										 |  |  |         this.modal = Modal.getOrCreateInstance(this.$widget[0]); | 
					
						
							| 
									
										
										
										
											2024-09-02 19:37:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |         this.$autoComplete = this.$widget.find(".jump-to-note-autocomplete"); | 
					
						
							| 
									
										
										
										
											2023-08-26 17:03:09 +03:00
										 |  |  |         this.$results = this.$widget.find(".jump-to-note-results"); | 
					
						
							| 
									
										
										
										
											2025-07-27 18:15:54 +03:00
										 |  |  |         this.$modalFooter = this.$widget.find(".modal-footer"); | 
					
						
							|  |  |  |         this.$modalFooter.find(".show-in-full-text-button").on("click", (e) => this.showInFullText(e)); | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         shortcutService.bindElShortcut(this.$widget, "ctrl+return", (e) => this.showInFullText(e)); | 
					
						
							| 
									
										
										
										
											2025-07-27 20:31:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 15:58:29 +03:00
										 |  |  |         // Monitor input changes to detect command mode switches
 | 
					
						
							|  |  |  |         this.$autoComplete.on("input", () => { | 
					
						
							|  |  |  |             this.updateCommandModeState(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private updateCommandModeState() { | 
					
						
							|  |  |  |         const currentValue = String(this.$autoComplete.val() || ""); | 
					
						
							|  |  |  |         const newCommandMode = currentValue.startsWith(">"); | 
					
						
							| 
									
										
										
										
											2025-07-27 20:31:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 15:58:29 +03:00
										 |  |  |         if (newCommandMode !== this.isCommandMode) { | 
					
						
							|  |  |  |             this.isCommandMode = newCommandMode; | 
					
						
							|  |  |  |             this.updateButtonVisibility(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private updateButtonVisibility() { | 
					
						
							|  |  |  |         if (this.isCommandMode) { | 
					
						
							| 
									
										
										
										
											2025-07-27 18:15:54 +03:00
										 |  |  |             this.$modalFooter.hide(); | 
					
						
							| 
									
										
										
										
											2025-07-27 15:58:29 +03:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2025-07-27 18:15:54 +03:00
										 |  |  |             this.$modalFooter.show(); | 
					
						
							| 
									
										
										
										
											2025-07-27 15:58:29 +03:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async jumpToNoteEvent() { | 
					
						
							| 
									
										
										
										
											2025-07-27 15:34:51 +03:00
										 |  |  |         await this.openDialog(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async commandPaletteEvent() { | 
					
						
							|  |  |  |         await this.openDialog(true); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private async openDialog(commandMode = false) { | 
					
						
							| 
									
										
										
										
											2025-06-19 22:44:02 +03:00
										 |  |  |         const dialogPromise = openDialog(this.$widget); | 
					
						
							| 
									
										
										
										
											2025-01-05 00:24:25 +02:00
										 |  |  |         if (utils.isMobile()) { | 
					
						
							|  |  |  |             dialogPromise.then(($dialog) => { | 
					
						
							|  |  |  |                 const el = $dialog.find(">.modal-dialog")[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 function reposition() { | 
					
						
							|  |  |  |                     const offset = 100; | 
					
						
							| 
									
										
										
										
											2025-03-19 23:06:16 +02:00
										 |  |  |                     const modalHeight = (window.visualViewport?.height ?? 0) - offset; | 
					
						
							|  |  |  |                     const safeAreaInsetBottom = (window.visualViewport?.height ?? 0) - window.innerHeight; | 
					
						
							| 
									
										
										
										
											2025-01-05 00:24:25 +02:00
										 |  |  |                     el.style.height = `${modalHeight}px`; | 
					
						
							| 
									
										
										
										
											2025-03-19 23:06:16 +02:00
										 |  |  |                     el.style.bottom = `${(window.visualViewport?.height ?? 0) - modalHeight - safeAreaInsetBottom - offset}px`; | 
					
						
							| 
									
										
										
										
											2025-01-05 00:24:25 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 this.$autoComplete.on("focus", () => { | 
					
						
							|  |  |  |                     reposition(); | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-19 23:06:16 +02:00
										 |  |  |                 window.visualViewport?.addEventListener("resize", () => { | 
					
						
							| 
									
										
										
										
											2025-01-05 00:24:25 +02:00
										 |  |  |                     reposition(); | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 reposition(); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // first open dialog, then refresh since refresh is doing focus which should be visible
 | 
					
						
							| 
									
										
										
										
											2025-07-27 15:34:51 +03:00
										 |  |  |         this.refresh(commandMode); | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         this.lastOpenedTs = Date.now(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 15:34:51 +03:00
										 |  |  |     async refresh(commandMode = false) { | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         noteAutocompleteService | 
					
						
							|  |  |  |             .initNoteAutocomplete(this.$autoComplete, { | 
					
						
							|  |  |  |                 allowCreatingNotes: true, | 
					
						
							|  |  |  |                 hideGoToSelectedNoteButton: true, | 
					
						
							|  |  |  |                 allowJumpToSearchNotes: true, | 
					
						
							| 
									
										
										
										
											2025-07-27 15:27:13 +03:00
										 |  |  |                 container: this.$results[0], | 
					
						
							|  |  |  |                 isCommandPalette: true | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |             // clear any event listener added in previous invocation of this function
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |             .off("autocomplete:noteselected") | 
					
						
							| 
									
										
										
										
											2025-07-27 15:27:13 +03:00
										 |  |  |             .off("autocomplete:commandselected") | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |             .on("autocomplete:noteselected", function (event, suggestion, dataset) { | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |                 if (!suggestion.notePath) { | 
					
						
							|  |  |  |                     return false; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-19 23:06:16 +02:00
										 |  |  |                 appContext.tabManager.getActiveContext()?.setNote(suggestion.notePath); | 
					
						
							| 
									
										
										
										
											2025-07-27 15:27:13 +03:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2025-07-27 15:30:27 +03:00
										 |  |  |             .on("autocomplete:commandselected", async (event, suggestion, dataset) => { | 
					
						
							| 
									
										
										
										
											2025-07-27 15:27:13 +03:00
										 |  |  |                 if (!suggestion.commandId) { | 
					
						
							|  |  |  |                     return false; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 await commandRegistry.executeCommand(suggestion.commandId); | 
					
						
							| 
									
										
										
										
											2025-07-27 15:30:27 +03:00
										 |  |  |                 this.modal.hide(); | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 15:34:51 +03:00
										 |  |  |         if (commandMode) { | 
					
						
							| 
									
										
										
										
											2025-07-27 15:42:44 +03:00
										 |  |  |             // Start in command mode - manually trigger command search
 | 
					
						
							|  |  |  |             this.$autoComplete.autocomplete("val", ">"); | 
					
						
							| 
									
										
										
										
											2025-07-27 15:58:29 +03:00
										 |  |  |             this.isCommandMode = true; | 
					
						
							|  |  |  |             this.updateButtonVisibility(); | 
					
						
							| 
									
										
										
										
											2025-07-27 20:31:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 15:42:44 +03:00
										 |  |  |             // Manually populate with all commands immediately
 | 
					
						
							|  |  |  |             noteAutocompleteService.showAllCommands(this.$autoComplete); | 
					
						
							| 
									
										
										
										
											2025-07-27 20:31:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 15:42:44 +03:00
										 |  |  |             this.$autoComplete.trigger("focus"); | 
					
						
							| 
									
										
										
										
											2025-07-27 15:34:51 +03:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             // if you open the Jump To dialog soon after using it previously, it can often mean that you
 | 
					
						
							|  |  |  |             // actually want to search for the same thing (e.g., you opened the wrong note at first try)
 | 
					
						
							|  |  |  |             // so we'll keep the content.
 | 
					
						
							|  |  |  |             // if it's outside of this time limit, then we assume it's a completely new search and show recent notes instead.
 | 
					
						
							|  |  |  |             if (Date.now() - this.lastOpenedTs > KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000) { | 
					
						
							| 
									
										
										
										
											2025-07-27 15:58:29 +03:00
										 |  |  |                 this.isCommandMode = false; | 
					
						
							|  |  |  |                 this.updateButtonVisibility(); | 
					
						
							| 
									
										
										
										
											2025-07-27 15:34:51 +03:00
										 |  |  |                 noteAutocompleteService.showRecentNotes(this.$autoComplete); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 this.$autoComplete | 
					
						
							|  |  |  |                     // hack, the actual search value is stored in <pre> element next to the search input
 | 
					
						
							|  |  |  |                     // this is important because the search input value is replaced with the suggestion note's title
 | 
					
						
							|  |  |  |                     .autocomplete("val", this.$autoComplete.next().text()) | 
					
						
							|  |  |  |                     .trigger("focus") | 
					
						
							|  |  |  |                     .trigger("select"); | 
					
						
							| 
									
										
										
										
											2025-07-27 20:31:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 15:58:29 +03:00
										 |  |  |                 // Update command mode state based on the restored value
 | 
					
						
							|  |  |  |                 this.updateCommandModeState(); | 
					
						
							| 
									
										
										
										
											2025-07-27 20:31:13 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 // If we restored a command mode value, manually trigger command display
 | 
					
						
							|  |  |  |                 if (this.isCommandMode) { | 
					
						
							|  |  |  |                     // Clear the value first, then set it to ">" to trigger a proper change
 | 
					
						
							|  |  |  |                     this.$autoComplete.autocomplete("val", ""); | 
					
						
							|  |  |  |                     noteAutocompleteService.showAllCommands(this.$autoComplete); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-07-27 15:34:51 +03:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-19 23:06:16 +02:00
										 |  |  |     showInFullText(e: JQuery.TriggeredEvent) { | 
					
						
							| 
									
										
										
										
											2023-06-30 11:18:34 +02:00
										 |  |  |         // stop from propagating upwards (dangerous, especially with ctrl+enter executable javascript notes)
 | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |         e.preventDefault(); | 
					
						
							|  |  |  |         e.stopPropagation(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-27 15:58:29 +03:00
										 |  |  |         // Don't perform full text search in command mode
 | 
					
						
							|  |  |  |         if (this.isCommandMode) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-19 23:06:16 +02:00
										 |  |  |         const searchString = String(this.$autoComplete.val()); | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         this.triggerCommand("searchNotes", { searchString }); | 
					
						
							| 
									
										
										
										
											2024-09-03 18:15:10 +02:00
										 |  |  |         this.modal.hide(); | 
					
						
							| 
									
										
										
										
											2022-06-16 10:42:49 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } |