| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  | import appContext from "../components/app_context.js"; | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  | import protectedSessionHolder from "./protected_session_holder.js"; | 
					
						
							|  |  |  | import server from "./server.js"; | 
					
						
							|  |  |  | import ws from "./ws.js"; | 
					
						
							| 
									
										
										
										
											2021-04-16 23:01:56 +02:00
										 |  |  | import froca from "./froca.js"; | 
					
						
							| 
									
										
										
										
											2021-03-03 22:48:06 +01:00
										 |  |  | import treeService from "./tree.js"; | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  | import toastService from "./toast.js"; | 
					
						
							| 
									
										
										
										
											2024-10-26 23:39:38 +03:00
										 |  |  | import { t } from "./i18n.js"; | 
					
						
							| 
									
										
										
										
											2025-01-13 23:18:10 +02:00
										 |  |  | import type FNote from "../entities/fnote.js"; | 
					
						
							|  |  |  | import type FBranch from "../entities/fbranch.js"; | 
					
						
							| 
									
										
										
										
											2025-01-09 18:36:24 +02:00
										 |  |  | import type { ChooseNoteTypeResponse } from "../widgets/dialogs/note_type_chooser.js"; | 
					
						
							| 
									
										
										
										
											2025-05-07 22:29:11 +03:00
										 |  |  | import type { CKTextEditor } from "@triliumnext/ckeditor5"; | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-13 13:06:53 +03:00
										 |  |  | export interface CreateNoteOpts { | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  |     isProtected?: boolean; | 
					
						
							|  |  |  |     saveSelection?: boolean; | 
					
						
							|  |  |  |     title?: string | null; | 
					
						
							|  |  |  |     content?: string | null; | 
					
						
							|  |  |  |     type?: string; | 
					
						
							|  |  |  |     mime?: string; | 
					
						
							|  |  |  |     templateNoteId?: string; | 
					
						
							|  |  |  |     activate?: boolean; | 
					
						
							|  |  |  |     focus?: "title" | "content"; | 
					
						
							|  |  |  |     target?: string; | 
					
						
							|  |  |  |     targetBranchId?: string; | 
					
						
							| 
									
										
										
										
											2025-05-07 22:29:11 +03:00
										 |  |  |     textEditor?: CKTextEditor; | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface Response { | 
					
						
							|  |  |  |     // TODO: Deduplicate with server once we have client/server architecture.
 | 
					
						
							|  |  |  |     note: FNote; | 
					
						
							|  |  |  |     branch: FBranch; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  | interface DuplicateResponse { | 
					
						
							|  |  |  |     // TODO: Deduplicate with server once we have client/server architecture.
 | 
					
						
							|  |  |  |     note: FNote; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-21 23:16:01 +02:00
										 |  |  | async function createNote(parentNotePath: string | undefined, options: CreateNoteOpts = {}) { | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     options = Object.assign( | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             activate: true, | 
					
						
							|  |  |  |             focus: "title", | 
					
						
							|  |  |  |             target: "into" | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         options | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-05 23:41:11 +02:00
										 |  |  |     // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted,
 | 
					
						
							|  |  |  |     // but this is quite weird since the user doesn't see WHERE the note is being created, so it shouldn't occur often
 | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |     if (!options.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()) { | 
					
						
							|  |  |  |         options.isProtected = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     if (appContext.tabManager.getActiveContextNoteType() !== "text") { | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |         options.saveSelection = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     if (options.saveSelection && options.textEditor) { | 
					
						
							| 
									
										
										
										
											2022-06-03 22:05:18 +02:00
										 |  |  |         [options.title, options.content] = parseSelectedHtml(options.textEditor.getSelectedHtml()); | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-29 22:37:19 +02:00
										 |  |  |     const parentNoteId = treeService.getNoteIdFromUrl(parentNotePath); | 
					
						
							| 
									
										
										
										
											2021-03-03 22:48:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-08 04:20:43 +02:00
										 |  |  |     if (options.type === "mermaid" && !options.content && !options.templateNoteId) { | 
					
						
							| 
									
										
										
										
											2021-09-30 10:18:03 +02:00
										 |  |  |         options.content = `graph TD;
 | 
					
						
							|  |  |  |     A-->B; | 
					
						
							|  |  |  |     A-->C; | 
					
						
							|  |  |  |     B-->D; | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     C-->D;`;
 | 
					
						
							| 
									
										
										
										
											2021-09-30 10:18:03 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     const { note, branch } = await server.post<Response>(`notes/${parentNoteId}/children?target=${options.target}&targetBranchId=${options.targetBranchId || ""}`, { | 
					
						
							| 
									
										
										
										
											2022-05-15 15:21:35 +02:00
										 |  |  |         title: options.title, | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |         content: options.content || "", | 
					
						
							|  |  |  |         isProtected: options.isProtected, | 
					
						
							| 
									
										
										
										
											2020-05-08 23:39:46 +02:00
										 |  |  |         type: options.type, | 
					
						
							| 
									
										
										
										
											2022-05-31 23:27:45 +02:00
										 |  |  |         mime: options.mime, | 
					
						
							|  |  |  |         templateNoteId: options.templateNoteId | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-03 22:05:18 +02:00
										 |  |  |     if (options.saveSelection) { | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |         // we remove the selection only after it was saved to server to make sure we don't lose anything
 | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  |         options.textEditor?.removeSelection(); | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-02 23:27:48 +02:00
										 |  |  |     await ws.waitForMaxKnownEntityChangeId(); | 
					
						
							| 
									
										
										
										
											2020-07-24 00:02:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-03 21:02:18 +01:00
										 |  |  |     const activeNoteContext = appContext.tabManager.getActiveContext(); | 
					
						
							|  |  |  |     if (activeNoteContext && options.activate) { | 
					
						
							| 
									
										
										
										
											2021-05-22 12:26:45 +02:00
										 |  |  |         await activeNoteContext.setNote(`${parentNotePath}/${note.noteId}`); | 
					
						
							| 
									
										
										
										
											2020-05-05 21:42:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         if (options.focus === "title") { | 
					
						
							|  |  |  |             appContext.triggerEvent("focusAndSelectTitle", { isNewNote: true }); | 
					
						
							|  |  |  |         } else if (options.focus === "content") { | 
					
						
							|  |  |  |             appContext.triggerEvent("focusOnDetail", { ntxId: activeNoteContext.ntxId }); | 
					
						
							| 
									
										
										
										
											2020-05-08 23:39:46 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-16 22:57:37 +02:00
										 |  |  |     const noteEntity = await froca.getNote(note.noteId); | 
					
						
							|  |  |  |     const branchEntity = froca.getBranch(branch.branchId); | 
					
						
							| 
									
										
										
										
											2020-05-09 14:25:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         note: noteEntity, | 
					
						
							|  |  |  |         branch: branchEntity | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 00:10:32 +02:00
										 |  |  | async function chooseNoteType() { | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     return new Promise<ChooseNoteTypeResponse>((res) => { | 
					
						
							|  |  |  |         appContext.triggerCommand("chooseNoteType", { callback: res }); | 
					
						
							| 
									
										
										
										
											2022-06-15 00:10:32 +02:00
										 |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  | async function createNoteWithTypePrompt(parentNotePath: string, options: CreateNoteOpts = {}) { | 
					
						
							| 
									
										
										
										
											2025-06-17 20:41:23 +08:00
										 |  |  |     const { success, noteType, templateNoteId, notePath } = await chooseNoteType(); | 
					
						
							| 
									
										
										
										
											2022-06-02 14:16:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!success) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     options.type = noteType; | 
					
						
							|  |  |  |     options.templateNoteId = templateNoteId; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 20:41:23 +08:00
										 |  |  |     return await createNote(notePath || parentNotePath, options); | 
					
						
							| 
									
										
										
										
											2022-06-02 14:16:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-05 23:41:11 +02:00
										 |  |  | /* If the first element is heading, parse it out and use it as a new heading. */ | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  | function parseSelectedHtml(selectedHtml: string) { | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |     const dom = $.parseHTML(selectedHtml); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  |     // TODO: tagName and outerHTML appear to be missing.
 | 
					
						
							|  |  |  |     //@ts-ignore
 | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |     if (dom.length > 0 && dom[0].tagName && dom[0].tagName.match(/h[1-6]/i)) { | 
					
						
							|  |  |  |         const title = $(dom[0]).text(); | 
					
						
							| 
									
										
										
										
											2023-06-23 00:26:47 +08:00
										 |  |  |         // remove the title from content (only first occurrence)
 | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  |         // TODO: tagName and outerHTML appear to be missing.
 | 
					
						
							|  |  |  |         //@ts-ignore
 | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |         const content = selectedHtml.replace(dom[0].outerHTML, ""); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return [title, content]; | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |         return [null, selectedHtml]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  | async function duplicateSubtree(noteId: string, parentNotePath: string) { | 
					
						
							| 
									
										
										
										
											2023-05-29 22:37:19 +02:00
										 |  |  |     const parentNoteId = treeService.getNoteIdFromUrl(parentNotePath); | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     const { note } = await server.post<DuplicateResponse>(`notes/${noteId}/duplicate/${parentNoteId}`); | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-02 23:27:48 +02:00
										 |  |  |     await ws.waitForMaxKnownEntityChangeId(); | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-03 21:02:18 +01:00
										 |  |  |     appContext.tabManager.getActiveContext()?.setNote(`${parentNotePath}/${note.noteId}`); | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-16 22:57:37 +02:00
										 |  |  |     const origNote = await froca.getNote(noteId); | 
					
						
							| 
									
										
										
										
											2024-12-21 23:13:30 +02:00
										 |  |  |     toastService.showMessage(t("note_create.duplicated", { title: origNote?.title })); | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default { | 
					
						
							|  |  |  |     createNote, | 
					
						
							| 
									
										
										
										
											2022-06-02 14:16:49 +02:00
										 |  |  |     createNoteWithTypePrompt, | 
					
						
							| 
									
										
										
										
											2022-06-15 00:10:32 +02:00
										 |  |  |     duplicateSubtree, | 
					
						
							|  |  |  |     chooseNoteType | 
					
						
							| 
									
										
										
										
											2020-05-09 14:25:27 +02:00
										 |  |  | }; |