refactor(create-note): centralize and add advanced type checking to create-note with and without prompt

This commit is contained in:
Jakob Schlanstedt 2025-10-21 18:38:31 +02:00
parent a15d661fd7
commit e41e24b044
12 changed files with 297 additions and 126 deletions

View File

@ -25,7 +25,7 @@ import TouchBarComponent from "./touch_bar.js";
import type { CKTextEditor } from "@triliumnext/ckeditor5"; import type { CKTextEditor } from "@triliumnext/ckeditor5";
import type CodeMirror from "@triliumnext/codemirror"; import type CodeMirror from "@triliumnext/codemirror";
import { StartupChecks } from "./startup_checks.js"; import { StartupChecks } from "./startup_checks.js";
import type { CreateNoteOpts } from "../services/note_create.js"; import type { BaseCreateNoteOpts } from "../services/note_create.js";
import { ColumnComponent } from "tabulator-tables"; import { ColumnComponent } from "tabulator-tables";
import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx"; import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx";
import type RootContainer from "../widgets/containers/root_container.js"; import type RootContainer from "../widgets/containers/root_container.js";
@ -359,7 +359,7 @@ export type CommandMappings = {
// Table view // Table view
addNewRow: CommandData & { addNewRow: CommandData & {
customOpts: CreateNoteOpts; customOpts: BaseCreateNoteOpts;
parentNotePath?: string; parentNotePath?: string;
}; };
addNewTableColumn: CommandData & { addNewTableColumn: CommandData & {

View File

@ -11,7 +11,7 @@ import froca from "../services/froca.js";
import linkService from "../services/link.js"; import linkService from "../services/link.js";
import { t } from "../services/i18n.js"; import { t } from "../services/i18n.js";
import { CreateChildrenResponse, SqlExecuteResponse } from "@triliumnext/commons"; import { CreateChildrenResponse, SqlExecuteResponse } from "@triliumnext/commons";
import noteCreateService from "../services/note_create.js"; import noteCreateService, { CreateNoteTarget } from "../services/note_create.js";
export default class Entrypoints extends Component { export default class Entrypoints extends Component {
constructor() { constructor() {
@ -25,7 +25,9 @@ export default class Entrypoints extends Component {
} }
async createNoteIntoInboxCommand() { async createNoteIntoInboxCommand() {
await noteCreateService.createNoteIntoInbox(); await noteCreateService.createNote(
CreateNoteTarget.IntoInbox
);
} }
async toggleNoteHoistingCommand({ noteId = appContext.tabManager.getActiveContextNoteId() }) { async toggleNoteHoistingCommand({ noteId = appContext.tabManager.getActiveContextNoteId() }) {

View File

@ -1,5 +1,5 @@
import appContext, { type EventData } from "./app_context.js"; import appContext, { type EventData } from "./app_context.js";
import noteCreateService from "../services/note_create.js"; import noteCreateService, { CreateNoteTarget, CreateNoteIntoURLOpts, CreateNoteAfterURLOpts } from "../services/note_create.js";
import treeService from "../services/tree.js"; import treeService from "../services/tree.js";
import hoistedNoteService from "../services/hoisted_note.js"; import hoistedNoteService from "../services/hoisted_note.js";
import Component from "./component.js"; import Component from "./component.js";
@ -48,10 +48,15 @@ export default class MainTreeExecutors extends Component {
return; return;
} }
await noteCreateService.createNoteIntoPath(activeNoteContext.notePath, { await noteCreateService.createNote(
CreateNoteTarget.IntoNoteURL,
{
parentNoteUrl: activeNoteContext.notePath,
isProtected: activeNoteContext.note.isProtected, isProtected: activeNoteContext.note.isProtected,
saveSelection: false saveSelection: false,
}); promptForType: false,
} as CreateNoteIntoURLOpts
);
} }
async createNoteAfterCommand() { async createNoteAfterCommand() {
@ -72,11 +77,14 @@ export default class MainTreeExecutors extends Component {
return; return;
} }
await noteCreateService.createNoteIntoPath(parentNotePath, { await noteCreateService.createNote(
target: "after", CreateNoteTarget.AfterNoteURL,
{
parentNoteUrl: parentNotePath,
targetBranchId: node.data.branchId, targetBranchId: node.data.branchId,
isProtected: isProtected, isProtected: isProtected,
saveSelection: false saveSelection: false
}); } as CreateNoteAfterURLOpts
);
} }
} }

View File

@ -8,7 +8,7 @@ import options from "../services/options.js";
import froca from "../services/froca.js"; import froca from "../services/froca.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import toastService from "../services/toast.js"; import toastService from "../services/toast.js";
import noteCreateService from "../services/note_create.js"; import noteCreateService, { CreateNoteIntoURLOpts, CreateNoteTarget } from "../services/note_create.js";
export default class RootCommandExecutor extends Component { export default class RootCommandExecutor extends Component {
editReadOnlyNoteCommand() { editReadOnlyNoteCommand() {
@ -240,14 +240,17 @@ export default class RootCommandExecutor extends Component {
// Create a new AI Chat note at the root level // Create a new AI Chat note at the root level
const rootNoteId = "root"; const rootNoteId = "root";
const result = await noteCreateService.createNoteIntoPath(rootNoteId, { const result = await noteCreateService.createNote(
CreateNoteTarget.IntoNoteURL,
{
title: "New AI Chat", title: "New AI Chat",
type: "aiChat", type: "aiChat",
content: JSON.stringify({ content: JSON.stringify({
messages: [], messages: [],
title: "New AI Chat" title: "New AI Chat"
}) }),
}); } as CreateNoteIntoURLOpts
);
if (!result.note) { if (!result.note) {
toastService.showError("Failed to create AI Chat note"); toastService.showError("Failed to create AI Chat note");

View File

@ -2,7 +2,7 @@ import NoteColorPicker from "./custom-items/NoteColorPicker.jsx";
import treeService from "../services/tree.js"; import treeService from "../services/tree.js";
import froca from "../services/froca.js"; import froca from "../services/froca.js";
import clipboard from "../services/clipboard.js"; import clipboard from "../services/clipboard.js";
import noteCreateService from "../services/note_create.js"; import noteCreateService, { CreateNoteAfterURLOpts, CreateNoteIntoURLOpts, CreateNoteTarget } from "../services/note_create.js";
import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js"; import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js";
import appContext, { type ContextMenuCommandData, type FilteredCommandNames } from "../components/app_context.js"; import appContext, { type ContextMenuCommandData, type FilteredCommandNames } from "../components/app_context.js";
import noteTypesService from "../services/note_types.js"; import noteTypesService from "../services/note_types.js";
@ -283,21 +283,32 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
const parentNotePath = treeService.getNotePath(this.node.getParent()); const parentNotePath = treeService.getNotePath(this.node.getParent());
const isProtected = treeService.getParentProtectedStatus(this.node); const isProtected = treeService.getParentProtectedStatus(this.node);
noteCreateService.createNoteIntoPath(parentNotePath, {
target: "after", noteCreateService.createNote(
CreateNoteTarget.AfterNoteURL,
{
parentNoteUrl: parentNotePath,
targetBranchId: this.node.data.branchId, targetBranchId: this.node.data.branchId,
type: type, type: type,
isProtected: isProtected, isProtected: isProtected,
templateNoteId: templateNoteId templateNoteId: templateNoteId,
}); promptForType: false,
} as CreateNoteAfterURLOpts
);
} else if (command === "insertChildNote") { } else if (command === "insertChildNote") {
const parentNotePath = treeService.getNotePath(this.node); const parentNotePath = treeService.getNotePath(this.node);
noteCreateService.createNoteIntoPath(parentNotePath, { noteCreateService.createNote(
CreateNoteTarget.IntoNoteURL,
{
parentNoteUrl: parentNotePath,
type: type, type: type,
isProtected: this.node.data.isProtected, isProtected: this.node.data.isProtected,
templateNoteId: templateNoteId templateNoteId: templateNoteId,
}); promptForType: false,
placement:
} as CreateNoteIntoURLOpts
);
} else if (command === "openNoteInSplit") { } else if (command === "openNoteInSplit") {
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts(); const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
const { ntxId } = subContexts?.[subContexts.length - 1] ?? {}; const { ntxId } = subContexts?.[subContexts.length - 1] ?? {};

View File

@ -1,6 +1,6 @@
import server from "./server.js"; import server from "./server.js";
import appContext from "../components/app_context.js"; import appContext from "../components/app_context.js";
import noteCreateService from "./note_create.js"; import noteCreateService, { CreateNoteIntoURLOpts, CreateNoteTarget, InboxNoteOpts } from "./note_create.js";
import froca from "./froca.js"; import froca from "./froca.js";
import { t } from "./i18n.js"; import { t } from "./i18n.js";
import commandRegistry from "./command_registry.js"; import commandRegistry from "./command_registry.js";
@ -481,15 +481,16 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
// --- CREATE NOTE INTO INBOX --- // --- CREATE NOTE INTO INBOX ---
case SuggestionAction.CreateNoteIntoInbox: { case SuggestionAction.CreateNoteIntoInbox: {
const { success, noteType, templateNoteId } = await noteCreateService.chooseNoteType(); const { note } = await noteCreateService.createNote(
if (!success) return; CreateNoteTarget.IntoInbox,
{
const { note } = await noteCreateService.createNoteIntoInbox({
title: suggestion.noteTitle, title: suggestion.noteTitle,
activate: true, activate: true,
type: noteType, promptForType: true,
templateNoteId, } as InboxNoteOpts
}); );
if (!note) return;
const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId; const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
suggestion.notePath = note?.getBestNotePathString(hoistedNoteId); suggestion.notePath = note?.getBestNotePathString(hoistedNoteId);
@ -501,15 +502,16 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
// --- CREATE AND LINK NOTE INTO INBOX --- // --- CREATE AND LINK NOTE INTO INBOX ---
case SuggestionAction.CreateAndLinkNoteIntoInbox: { case SuggestionAction.CreateAndLinkNoteIntoInbox: {
const { success, noteType, templateNoteId } = await noteCreateService.chooseNoteType(); const { note } = await noteCreateService.createNote(
if (!success) return; CreateNoteTarget.IntoInbox,
{
const { note } = await noteCreateService.createNoteIntoInbox({
title: suggestion.noteTitle, title: suggestion.noteTitle,
activate: false, activate: false,
type: noteType, promptForType: true,
templateNoteId, } as InboxNoteOpts,
}); );
if (!note) return;
const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId; const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
suggestion.notePath = note?.getBestNotePathString(hoistedNoteId); suggestion.notePath = note?.getBestNotePathString(hoistedNoteId);
@ -521,15 +523,17 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
// --- CREATE NOTE INTO PATH --- // --- CREATE NOTE INTO PATH ---
case SuggestionAction.CreateNoteIntoPath: { case SuggestionAction.CreateNoteIntoPath: {
const { success, noteType, templateNoteId, notePath } = await noteCreateService.chooseNoteType(); const { note } = await noteCreateService.createNote(
if (!success) return; CreateNoteTarget.IntoNoteURL,
{
const { note } = await noteCreateService.createNoteIntoPath(notePath || suggestion.parentNoteId, { parentNoteUrl: suggestion.parentNoteId,
title: suggestion.noteTitle, title: suggestion.noteTitle,
activate: true, activate: true,
type: noteType, promptForType: true,
templateNoteId, } as CreateNoteIntoURLOpts
}); )
if (!note) return;
const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId; const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
suggestion.notePath = note?.getBestNotePathString(hoistedNoteId); suggestion.notePath = note?.getBestNotePathString(hoistedNoteId);
@ -541,15 +545,16 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
// --- CREATE AND LINK NOTE INTO PATH --- // --- CREATE AND LINK NOTE INTO PATH ---
case SuggestionAction.CreateAndLinkNoteIntoPath: { case SuggestionAction.CreateAndLinkNoteIntoPath: {
const { success, noteType, templateNoteId, notePath } = await noteCreateService.chooseNoteType(); const { note } = await noteCreateService.createNote(
if (!success) return; CreateNoteTarget.IntoNoteURL,
{
const { note } = await noteCreateService.createNoteIntoPath(notePath || suggestion.parentNoteId, {
title: suggestion.noteTitle, title: suggestion.noteTitle,
activate: false, activate: false,
type: noteType, promptForType: true,
templateNoteId, } as CreateNoteIntoURLOpts
}); );
if (!note) return
const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId; const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
suggestion.notePath = note?.getBestNotePathString(hoistedNoteId); suggestion.notePath = note?.getBestNotePathString(hoistedNoteId);

View File

@ -13,7 +13,30 @@ import type { CKTextEditor } from "@triliumnext/ckeditor5";
import dateNoteService from "../services/date_notes.js"; import dateNoteService from "../services/date_notes.js";
import { CreateChildrenResponse } from "@triliumnext/commons"; import { CreateChildrenResponse } from "@triliumnext/commons";
export interface CreateNoteOpts { // // Creating a note at a path creates ambiguity, do we want it created Into or
// // Next to as sibling?
// // TODO: where the heck is this defined
// export enum NotePlacement {
// Into = "into",
// After = "after"
// }
export enum CreateNoteTarget {
IntoNoteURL,
AfterNoteURL,
IntoInbox,
}
export type BaseCreateNoteOpts =
| ({
promptForType: true;
type?: never;
} & BaseCreateNoteSharedOpts)
| ({
promptForType?: false;
type?: string;
} & BaseCreateNoteSharedOpts);
export interface BaseCreateNoteSharedOpts {
isProtected?: boolean; isProtected?: boolean;
saveSelection?: boolean; saveSelection?: boolean;
title?: string | null; title?: string | null;
@ -23,11 +46,30 @@ export interface CreateNoteOpts {
templateNoteId?: string; templateNoteId?: string;
activate?: boolean; activate?: boolean;
focus?: "title" | "content"; focus?: "title" | "content";
target?: string;
targetBranchId?: string; targetBranchId?: string;
textEditor?: CKTextEditor; textEditor?: CKTextEditor;
} }
// For creating *in a specific path*
type CreateNoteAtURLOpts = BaseCreateNoteSharedOpts & {
// Url is either path or Id
parentNoteUrl: string;
}
export type CreateNoteIntoURLOpts = CreateNoteAtURLOpts;
export type CreateNoteAfterURLOpts = Omit<CreateNoteAtURLOpts, "targetBranchId"> & {
// targetBranchId disambiguates the position for cloned notes, thus it must
// only be specified for a sibling
// This is also specified in the backend
targetBranchId: string;
};
// For creating *in the inbox*
export type InboxNoteOpts = BaseCreateNoteSharedOpts & {
// disallowed
parentNoteUrl?: never;
}
interface Response { interface Response {
// TODO: Deduplicate with server once we have client/server architecture. // TODO: Deduplicate with server once we have client/server architecture.
note: FNote; note: FNote;
@ -42,14 +84,15 @@ interface DuplicateResponse {
/** /**
* Core function that creates a new note under the specified parent note path. * Core function that creates a new note under the specified parent note path.
* *
* @param {string | undefined} parentNotePath - The parent note path where the new note will be created. * @param target - Duplicates apps/server/src/routes/api/notes.ts createNote
* @param {CreateNoteOpts} [options] - Options controlling note creation (title, content, type, template, focus, etc.). * @param {BaseCreateNoteSharedOpts} [options] - Options controlling note creation (title, content, type, template, focus, etc.).
* with parentNotePath - The parent note path where the new note will be created.
* @returns {Promise<{ note: FNote | null; branch: FBranch | undefined }>} * @returns {Promise<{ note: FNote | null; branch: FBranch | undefined }>}
* Resolves with the created note and branch entities. * Resolves with the created note and branch entities.
*/ */
async function createNoteIntoPath( async function createNoteAtNote(
parentNotePath: string | undefined, target: "into" | "after" | "before",
options: CreateNoteOpts = {} options: CreateNoteAtURLOpts
): Promise<{ note: FNote | null; branch: FBranch | undefined }> { ): Promise<{ note: FNote | null; branch: FBranch | undefined }> {
options = Object.assign( options = Object.assign(
{ {
@ -74,7 +117,8 @@ async function createNoteIntoPath(
[options.title, options.content] = parseSelectedHtml(options.textEditor.getSelectedHtml()); [options.title, options.content] = parseSelectedHtml(options.textEditor.getSelectedHtml());
} }
const parentNoteId = treeService.getNoteIdFromUrl(parentNotePath); const parentNoteUrl = options.parentNoteUrl;
const parentNoteId = treeService.getNoteIdFromUrl(parentNoteUrl);
if (options.type === "mermaid" && !options.content && !options.templateNoteId) { if (options.type === "mermaid" && !options.content && !options.templateNoteId) {
options.content = `graph TD; options.content = `graph TD;
@ -84,7 +128,7 @@ async function createNoteIntoPath(
C-->D;`; C-->D;`;
} }
const { note, branch } = await server.post<Response>(`notes/${parentNoteId}/children?target=${options.target}&targetBranchId=${options.targetBranchId || ""}`, { const { note, branch } = await server.post<Response>(`notes/${parentNoteId}/children?target=${target}&targetBranchId=${options.targetBranchId || ""}`, {
title: options.title, title: options.title,
content: options.content || "", content: options.content || "",
isProtected: options.isProtected, isProtected: options.isProtected,
@ -102,7 +146,7 @@ async function createNoteIntoPath(
const activeNoteContext = appContext.tabManager.getActiveContext(); const activeNoteContext = appContext.tabManager.getActiveContext();
if (activeNoteContext && options.activate) { if (activeNoteContext && options.activate) {
await activeNoteContext.setNote(`${parentNotePath}/${note.noteId}`); await activeNoteContext.setNote(`${parentNoteId}/${note.noteId}`);
if (options.focus === "title") { if (options.focus === "title") {
appContext.triggerEvent("focusAndSelectTitle", { isNewNote: true }); appContext.triggerEvent("focusAndSelectTitle", { isNewNote: true });
@ -120,15 +164,27 @@ async function createNoteIntoPath(
}; };
} }
async function createNoteIntoNote(
options: CreateNoteIntoURLOpts
): Promise<{ note: FNote | null; branch: FBranch | undefined }> {
return createNoteAtNote("into", {...options} as CreateNoteAtURLOpts);
}
async function createNoteAfterNote(
options: CreateNoteAfterURLOpts
): Promise<{ note: FNote | null; branch: FBranch | undefined }> {
return createNoteAtNote("after", {...options} as CreateNoteAtURLOpts);
}
/** /**
* Creates a new note inside the user's Inbox. * Creates a new note inside the user's Inbox.
* *
* @param {CreateNoteOpts} [options] - Optional settings such as title, type, template, or content. * @param {BaseCreateNoteSharedOpts} [options] - Optional settings such as title, type, template, or content.
* @returns {Promise<{ note: FNote | null; branch: FBranch | undefined }>} * @returns {Promise<{ note: FNote | null; branch: FBranch | undefined }>}
* Resolves with the created note and its branch, or `{ note: null, branch: undefined }` if the inbox is missing. * Resolves with the created note and its branch, or `{ note: null, branch: undefined }` if the inbox is missing.
*/ */
async function createNoteIntoInbox( async function createNoteIntoInbox(
options: CreateNoteOpts = {} options: InboxNoteOpts
): Promise<{ note: FNote | null; branch: FBranch | undefined }> { ): Promise<{ note: FNote | null; branch: FBranch | undefined }> {
const inboxNote = await dateNoteService.getInboxNote(); const inboxNote = await dateNoteService.getInboxNote();
if (!inboxNote) { if (!inboxNote) {
@ -142,10 +198,12 @@ async function createNoteIntoInbox(
inboxNote.isProtected && protectedSessionHolder.isProtectedSessionAvailable(); inboxNote.isProtected && protectedSessionHolder.isProtectedSessionAvailable();
} }
const result = await createNoteIntoPath(inboxNote.noteId, { const result = await createNoteIntoNote(
{
...options, ...options,
target: "into", parentNoteUrl: inboxNote.noteId,
}); } as CreateNoteIntoURLOpts
);
return result; return result;
} }
@ -156,17 +214,70 @@ async function chooseNoteType() {
}); });
} }
async function createNoteIntoPathWithTypePrompt(parentNotePath: string, options: CreateNoteOpts = {}) { async function createNote(
target: CreateNoteTarget.IntoNoteURL,
options: CreateNoteIntoURLOpts
): Promise<{ note: FNote | null; branch: FBranch | undefined }>;
async function createNote(
target: CreateNoteTarget.AfterNoteURL,
options: CreateNoteAfterURLOpts
): Promise<{ note: FNote | null; branch: FBranch | undefined }>;
async function createNote(
target: CreateNoteTarget.IntoInbox,
options?: InboxNoteOpts
): Promise<{ note: FNote | null; branch: FBranch | undefined }>;
async function createNote(
target: CreateNoteTarget,
options: BaseCreateNoteOpts = {promptForType: true}
): Promise<{ note: FNote | null; branch: FBranch | undefined }> {
let resolvedOptions = { ...options };
let resolvedTarget = target;
// handle prompts centrally to write once fix for all
if (options.promptForType) {
const { success, noteType, templateNoteId, notePath } = await chooseNoteType(); const { success, noteType, templateNoteId, notePath } = await chooseNoteType();
if (!success) { if (!success) return {
return; note: null, branch: undefined
};
resolvedOptions = {
...resolvedOptions,
promptForType: false,
type: noteType,
templateNoteId,
} as BaseCreateNoteOpts;
if (notePath) {
resolvedOptions = resolvedOptions as CreateNoteIntoURLOpts;
resolvedOptions = {
...resolvedOptions,
parentNoteUrl: notePath,
} as CreateNoteIntoURLOpts;
resolvedTarget = CreateNoteTarget.IntoNoteURL;
}
} }
options.type = noteType; switch (resolvedTarget) {
options.templateNoteId = templateNoteId; case CreateNoteTarget.IntoNoteURL:
return await createNoteIntoNote(resolvedOptions as CreateNoteIntoURLOpts);
return await createNoteIntoPath(notePath || parentNotePath, options); case CreateNoteTarget.AfterNoteURL:
return await createNoteAfterNote(resolvedOptions as CreateNoteAfterURLOpts);
case CreateNoteTarget.IntoInbox:
return await createNoteIntoInbox(resolvedOptions as InboxNoteOpts);
default: {
console.warn("[createNote] Unknown target:", target, resolvedOptions);
toastService.showMessage("Unknown note creation target."); // optional
return { note: null, branch: undefined };
}
}
} }
/* If the first element is heading, parse it out and use it as a new heading. */ /* If the first element is heading, parse it out and use it as a new heading. */
@ -201,9 +312,6 @@ async function duplicateSubtree(noteId: string, parentNotePath: string) {
} }
export default { export default {
createNoteIntoInbox, createNote,
createNoteIntoPath,
createNoteIntoPathWithTypePrompt,
duplicateSubtree, duplicateSubtree,
chooseNoteType
}; };

View File

@ -7,7 +7,7 @@ import branches from "../../../services/branches";
import { executeBulkActions } from "../../../services/bulk_action"; import { executeBulkActions } from "../../../services/bulk_action";
import froca from "../../../services/froca"; import froca from "../../../services/froca";
import { t } from "../../../services/i18n"; import { t } from "../../../services/i18n";
import note_create from "../../../services/note_create.js"; import note_create, { CreateNoteIntoURLOpts, CreateNoteTarget } from "../../../services/note_create.js";
import server from "../../../services/server"; import server from "../../../services/server";
import { ColumnMap } from "./data"; import { ColumnMap } from "./data";
@ -39,10 +39,11 @@ export default class BoardApi {
const parentNotePath = this.parentNote.noteId; const parentNotePath = this.parentNote.noteId;
// Create a new note as a child of the parent note // Create a new note as a child of the parent note
const { note: newNote, branch: newBranch } = await note_create.createNoteIntoPath(parentNotePath, { const { note: newNote, branch: newBranch } = await note_create.createNote(CreateNoteTarget.IntoNoteURL, {
parentNoteUrl: parentNotePath,
activate: false, activate: false,
title title,
}); } as CreateNoteIntoURLOpts);
if (newNote && newBranch) { if (newNote && newBranch) {
await this.changeColumn(newNote.noteId, column); await this.changeColumn(newNote.noteId, column);
@ -140,12 +141,16 @@ export default class BoardApi {
column: string, column: string,
relativeToBranchId: string, relativeToBranchId: string,
direction: "before" | "after") { direction: "before" | "after") {
const { note, branch } = await note_create.createNoteIntoPath(this.parentNote.noteId, { const { note, branch } = await note_create.createNote(
CreateNoteTarget.IntoNoteURL,
{
parentNoteUrl: this.parentNote.noteId,
activate: false, activate: false,
targetBranchId: relativeToBranchId, targetBranchId: relativeToBranchId,
target: direction, target: direction,
title: t("board_view.new-item") title: t("board_view.new-item"),
}); } as CreateNoteIntoURLOpts
);
if (!note || !branch) { if (!note || !branch) {
throw new Error("Failed to create note"); throw new Error("Failed to create note");

View File

@ -1,6 +1,6 @@
import { EventCallBackMethods, RowComponent, Tabulator } from "tabulator-tables"; import { EventCallBackMethods, RowComponent, Tabulator } from "tabulator-tables";
import { CommandListenerData } from "../../../components/app_context"; import { CommandListenerData } from "../../../components/app_context";
import note_create, { CreateNoteOpts } from "../../../services/note_create"; import note_create, { BaseCreateNoteOpts, CreateNoteIntoURLOpts as CreateNoteIntoURLOpts, CreateNoteTarget } from "../../../services/note_create";
import { useLegacyImperativeHandlers } from "../../react/hooks"; import { useLegacyImperativeHandlers } from "../../react/hooks";
import { RefObject } from "preact"; import { RefObject } from "preact";
import { setAttribute, setLabel } from "../../../services/attributes"; import { setAttribute, setLabel } from "../../../services/attributes";
@ -15,11 +15,17 @@ export default function useRowTableEditing(api: RefObject<Tabulator>, attributeD
addNewRowCommand({ customOpts, parentNotePath: customNotePath }: CommandListenerData<"addNewRow">) { addNewRowCommand({ customOpts, parentNotePath: customNotePath }: CommandListenerData<"addNewRow">) {
const notePath = customNotePath ?? parentNotePath; const notePath = customNotePath ?? parentNotePath;
if (notePath) { if (notePath) {
const opts: CreateNoteOpts = { const opts: BaseCreateNoteOpts = {
activate: false, activate: false,
...customOpts ...customOpts
} }
note_create.createNoteIntoPath(notePath, opts).then(({ branch }) => { note_create.createNote(
CreateNoteTarget.IntoNoteURL,
{
parentNoteUrl: notePath,
...opts
} as CreateNoteIntoURLOpts
).then(({ branch }) => {
if (branch) { if (branch) {
setTimeout(() => { setTimeout(() => {
if (!api.current) return; if (!api.current) return;

View File

@ -3,7 +3,7 @@ import appContext from "../../components/app_context";
import contextMenu from "../../menus/context_menu"; import contextMenu from "../../menus/context_menu";
import branches from "../../services/branches"; import branches from "../../services/branches";
import { t } from "../../services/i18n"; import { t } from "../../services/i18n";
import note_create from "../../services/note_create"; import note_create, { CreateNoteIntoURLOpts, CreateNoteTarget } from "../../services/note_create";
import tree from "../../services/tree"; import tree from "../../services/tree";
import ActionButton from "../react/ActionButton"; import ActionButton from "../react/ActionButton";
import { ParentComponent } from "../react/react_utils"; import { ParentComponent } from "../react/react_utils";
@ -29,7 +29,12 @@ export default function MobileDetailMenu() {
], ],
selectMenuItemHandler: async ({ command }) => { selectMenuItemHandler: async ({ command }) => {
if (command === "insertChildNote") { if (command === "insertChildNote") {
note_create.createNoteIntoPath(appContext.tabManager.getActiveContextNotePath() ?? undefined); note_create.createNote(
CreateNoteTarget.IntoNoteURL,
{
parentNoteUrl: appContext.tabManager.getActiveContextNotePath() ?? undefined
} as CreateNoteIntoURLOpts
);
} else if (command === "delete") { } else if (command === "delete") {
const notePath = appContext.tabManager.getActiveContextNotePath(); const notePath = appContext.tabManager.getActiveContextNotePath();
if (!notePath) { if (!notePath) {

View File

@ -7,7 +7,7 @@ import branchService from "../services/branches.js";
import ws from "../services/ws.js"; import ws from "../services/ws.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js"; import NoteContextAwareWidget from "./note_context_aware_widget.js";
import server from "../services/server.js"; import server from "../services/server.js";
import noteCreateService from "../services/note_create.js"; import noteCreateService, { CreateNoteIntoURLOpts, CreateNoteTarget } from "../services/note_create.js";
import toastService from "../services/toast.js"; import toastService from "../services/toast.js";
import appContext, { type CommandListenerData, type EventData } from "../components/app_context.js"; import appContext, { type CommandListenerData, type EventData } from "../components/app_context.js";
import keyboardActionsService from "../services/keyboard_actions.js"; import keyboardActionsService from "../services/keyboard_actions.js";
@ -224,7 +224,13 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
} else if (target.classList.contains("add-note-button")) { } else if (target.classList.contains("add-note-button")) {
const node = $.ui.fancytree.getNode(e as unknown as Event); const node = $.ui.fancytree.getNode(e as unknown as Event);
const parentNotePath = treeService.getNotePath(node); const parentNotePath = treeService.getNotePath(node);
noteCreateService.createNoteIntoPath(parentNotePath, { isProtected: node.data.isProtected }); noteCreateService.createNote(
CreateNoteTarget.IntoNoteURL,
{
parentNoteUrl: parentNotePath,
isProtected: node.data.isProtected
} as CreateNoteIntoURLOpts,
);
} else if (target.classList.contains("enter-workspace-button")) { } else if (target.classList.contains("enter-workspace-button")) {
const node = $.ui.fancytree.getNode(e as unknown as Event); const node = $.ui.fancytree.getNode(e as unknown as Event);
this.triggerCommand("hoistNote", { noteId: node.data.noteId }); this.triggerCommand("hoistNote", { noteId: node.data.noteId });
@ -1836,9 +1842,13 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
const node = this.getActiveNode(); const node = this.getActiveNode();
if (!node) return; if (!node) return;
const notePath = treeService.getNotePath(node); const notePath = treeService.getNotePath(node);
noteCreateService.createNoteIntoPath(notePath, { noteCreateService.createNote(
CreateNoteTarget.IntoNoteURL,
{
parentNoteUrl: notePath,
isProtected: node.data.isProtected isProtected: node.data.isProtected
}); } as CreateNoteIntoURLOpts
)
} }
}), }),
new TouchBar.TouchBarButton({ new TouchBar.TouchBarButton({

View File

@ -19,7 +19,7 @@ import contextMenu from "../../../menus/context_menu";
import type { CommandData, FilteredCommandNames } from "../../../components/app_context"; import type { CommandData, FilteredCommandNames } from "../../../components/app_context";
import { AttributeType } from "@triliumnext/commons"; import { AttributeType } from "@triliumnext/commons";
import attributes from "../../../services/attributes"; import attributes from "../../../services/attributes";
import note_create from "../../../services/note_create"; import note_create, { CreateNoteAfterURLOpts, CreateNoteIntoURLOpts, CreateNoteTarget, InboxNoteOpts } from "../../../services/note_create";
import { CreateNoteAction } from "@triliumnext/commons"; import { CreateNoteAction } from "@triliumnext/commons";
type AttributeCommandNames = FilteredCommandNames<CommandData>; type AttributeCommandNames = FilteredCommandNames<CommandData>;
@ -261,19 +261,27 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
switch (action) { switch (action) {
case CreateNoteAction.CreateNoteIntoInbox: case CreateNoteAction.CreateNoteIntoInbox:
case CreateNoteAction.CreateAndLinkNoteIntoInbox: { case CreateNoteAction.CreateAndLinkNoteIntoInbox: {
const { note } = await note_create.createNoteIntoInbox({ const { note } = await note_create.createNote(
CreateNoteTarget.IntoInbox,
{
title, title,
activate: false activate: false
}); } as InboxNoteOpts
);
return note?.getBestNotePathString() ?? ""; return note?.getBestNotePathString() ?? "";
} }
case CreateNoteAction.CreateNoteIntoPath: case CreateNoteAction.CreateNoteIntoPath:
case CreateNoteAction.CreateAndLinkNoteIntoPath: { case CreateNoteAction.CreateAndLinkNoteIntoPath: {
const resp = await note_create.createNoteIntoPathWithTypePrompt(parentNotePath, { const resp = await note_create.createNote(
CreateNoteTarget.IntoNoteURL,
{
parentNoteUrl: parentNotePath,
title, title,
activate: false activate: false,
}); promptForType: true,
} as CreateNoteIntoURLOpts,
)
return resp?.note?.getBestNotePathString() ?? ""; return resp?.note?.getBestNotePathString() ?? "";
} }