mirror of
https://github.com/zadam/trilium.git
synced 2025-12-04 22:44:25 +01:00
Compare commits
67 Commits
7f4d8bc8ca
...
a364d1a993
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a364d1a993 | ||
|
|
0b28159e8e | ||
|
|
d9e8f8e69b | ||
|
|
fa224e46bc | ||
|
|
06320953e8 | ||
|
|
d676084cb3 | ||
|
|
0cb5941be0 | ||
|
|
8ee59e9daa | ||
|
|
4e5c26371e | ||
|
|
436146d829 | ||
|
|
315fcecf57 | ||
|
|
0a57e6e154 | ||
|
|
bdcb84a394 | ||
|
|
213c36ba84 | ||
|
|
995e765276 | ||
|
|
8e81c38c14 | ||
|
|
7378fa4cbd | ||
|
|
af8a5ff0c9 | ||
|
|
eca5a4a072 | ||
|
|
71b3ad5027 | ||
|
|
7864168adc | ||
|
|
f8090d9217 | ||
|
|
09aa22c74b | ||
|
|
8fc8f97879 | ||
|
|
547cdff510 | ||
|
|
cdd08d6971 | ||
|
|
b3cf9c8f2d | ||
|
|
feefa389b4 | ||
|
|
f65be4f368 | ||
|
|
77a014109e | ||
|
|
d3db48c99b | ||
|
|
7e4833e893 | ||
|
|
470d7eee31 | ||
|
|
aada631c0f | ||
|
|
bc4186d216 | ||
|
|
c2a27eff2c | ||
|
|
ca24408a13 | ||
|
|
b9e19e524a | ||
|
|
09c8a778f5 | ||
|
|
3438f1103d | ||
|
|
82a3be06d1 | ||
|
|
f0dead5390 | ||
|
|
b0fdb9fef2 | ||
|
|
71009bddc7 | ||
|
|
66e499a2e1 | ||
|
|
a5ef5eee2f | ||
|
|
bcb29d22f5 | ||
|
|
6ad2b49ab3 | ||
|
|
656e7c069d | ||
|
|
00aa470bf2 | ||
|
|
c6ed0b43fc | ||
|
|
3d8a4d2553 | ||
|
|
42ab0eb804 | ||
|
|
d80d06a9b8 | ||
|
|
3c39026739 | ||
|
|
72c17b22df | ||
|
|
dd1aa23cb6 | ||
|
|
ecdf243e63 | ||
|
|
1e15585a24 | ||
|
|
9d40c0cb26 | ||
|
|
e41e24b044 | ||
|
|
a15d661fd7 | ||
|
|
cb5954e8c7 | ||
|
|
2b55db05e1 | ||
|
|
74bf93059c | ||
|
|
384d8c9c37 | ||
|
|
1bb6149dbe |
@ -16,7 +16,7 @@
|
||||
"fs-extra": "11.3.2",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"typedoc": "0.28.14",
|
||||
"typedoc": "0.28.15",
|
||||
"typedoc-plugin-missing-exports": "4.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ import TouchBarComponent from "./touch_bar.js";
|
||||
import type { CKTextEditor } from "@triliumnext/ckeditor5";
|
||||
import type CodeMirror from "@triliumnext/codemirror";
|
||||
import { StartupChecks } from "./startup_checks.js";
|
||||
import type { CreateNoteOpts } from "../services/note_create.js";
|
||||
import type { CreateNoteOpts, CreateNoteWithLinkOpts } from "../services/note_create.js";
|
||||
import { ColumnComponent } from "tabulator-tables";
|
||||
import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx";
|
||||
import type RootContainer from "../widgets/containers/root_container.js";
|
||||
@ -359,8 +359,7 @@ export type CommandMappings = {
|
||||
|
||||
// Table view
|
||||
addNewRow: CommandData & {
|
||||
customOpts: CreateNoteOpts;
|
||||
parentNotePath?: string;
|
||||
customOpts?: CreateNoteWithLinkOpts;
|
||||
};
|
||||
addNewTableColumn: CommandData & {
|
||||
columnToEdit?: ColumnComponent;
|
||||
|
||||
@ -11,6 +11,7 @@ import froca from "../services/froca.js";
|
||||
import linkService from "../services/link.js";
|
||||
import { t } from "../services/i18n.js";
|
||||
import { CreateChildrenResponse, SqlExecuteResponse } from "@triliumnext/commons";
|
||||
import noteCreateService from "../services/note_create.js";
|
||||
|
||||
export default class Entrypoints extends Component {
|
||||
constructor() {
|
||||
@ -24,23 +25,9 @@ export default class Entrypoints extends Component {
|
||||
}
|
||||
|
||||
async createNoteIntoInboxCommand() {
|
||||
const inboxNote = await dateNoteService.getInboxNote();
|
||||
if (!inboxNote) {
|
||||
console.warn("Missing inbox note.");
|
||||
return;
|
||||
}
|
||||
|
||||
const { note } = await server.post<CreateChildrenResponse>(`notes/${inboxNote.noteId}/children?target=into`, {
|
||||
content: "",
|
||||
type: "text",
|
||||
isProtected: inboxNote.isProtected && protectedSessionHolder.isProtectedSessionAvailable()
|
||||
});
|
||||
|
||||
await ws.waitForMaxKnownEntityChangeId();
|
||||
|
||||
await appContext.tabManager.openTabWithNoteWithHoisting(note.noteId, { activate: true });
|
||||
|
||||
appContext.triggerEvent("focusAndSelectTitle", { isNewNote: true });
|
||||
await noteCreateService.createNote(
|
||||
{ target: "default" }
|
||||
);
|
||||
}
|
||||
|
||||
async toggleNoteHoistingCommand({ noteId = appContext.tabManager.getActiveContextNoteId() }) {
|
||||
|
||||
@ -48,10 +48,15 @@ export default class MainTreeExecutors extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
await noteCreateService.createNote(activeNoteContext.notePath, {
|
||||
await noteCreateService.createNote(
|
||||
{
|
||||
target: "into",
|
||||
parentNoteLink: activeNoteContext.notePath,
|
||||
isProtected: activeNoteContext.note.isProtected,
|
||||
saveSelection: false
|
||||
});
|
||||
saveSelection: false,
|
||||
promptForType: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async createNoteAfterCommand() {
|
||||
@ -72,11 +77,14 @@ export default class MainTreeExecutors extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
await noteCreateService.createNote(parentNotePath, {
|
||||
await noteCreateService.createNote(
|
||||
{
|
||||
target: "after",
|
||||
parentNoteLink: parentNotePath,
|
||||
targetBranchId: node.data.branchId,
|
||||
isProtected: isProtected,
|
||||
saveSelection: false
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ export default class RootCommandExecutor extends Component {
|
||||
}
|
||||
|
||||
async searchInSubtreeCommand({ notePath }: CommandListenerData<"searchInSubtree">) {
|
||||
const noteId = treeService.getNoteIdFromUrl(notePath);
|
||||
const noteId = treeService.getNoteIdFromLink(notePath);
|
||||
|
||||
this.searchNotesCommand({ ancestorNoteId: noteId });
|
||||
}
|
||||
@ -240,14 +240,18 @@ export default class RootCommandExecutor extends Component {
|
||||
// Create a new AI Chat note at the root level
|
||||
const rootNoteId = "root";
|
||||
|
||||
const result = await noteCreateService.createNote(rootNoteId, {
|
||||
const result = await noteCreateService.createNote(
|
||||
{
|
||||
parentNoteLink: rootNoteId,
|
||||
target: "into",
|
||||
title: "New AI Chat",
|
||||
type: "aiChat",
|
||||
content: JSON.stringify({
|
||||
messages: [],
|
||||
title: "New AI Chat"
|
||||
})
|
||||
});
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!result.note) {
|
||||
toastService.showError("Failed to create AI Chat note");
|
||||
|
||||
@ -74,10 +74,10 @@ export default class TabManager extends Component {
|
||||
|
||||
// preload all notes at once
|
||||
await froca.getNotes([...noteContextsToOpen.flatMap((tab: NoteContextState) =>
|
||||
[treeService.getNoteIdFromUrl(tab.notePath), tab.hoistedNoteId])], true);
|
||||
[treeService.getNoteIdFromLink(tab.notePath), tab.hoistedNoteId])], true);
|
||||
|
||||
const filteredNoteContexts = noteContextsToOpen.filter((openTab: NoteContextState) => {
|
||||
const noteId = treeService.getNoteIdFromUrl(openTab.notePath);
|
||||
const noteId = treeService.getNoteIdFromLink(openTab.notePath);
|
||||
if (!noteId || !(noteId in froca.notes)) {
|
||||
// note doesn't exist so don't try to open tab for it
|
||||
return false;
|
||||
|
||||
@ -283,21 +283,31 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
|
||||
const parentNotePath = treeService.getNotePath(this.node.getParent());
|
||||
const isProtected = treeService.getParentProtectedStatus(this.node);
|
||||
|
||||
noteCreateService.createNote(parentNotePath, {
|
||||
|
||||
noteCreateService.createNote(
|
||||
{
|
||||
target: "after",
|
||||
parentNoteLink: parentNotePath,
|
||||
targetBranchId: this.node.data.branchId,
|
||||
type: type,
|
||||
isProtected: isProtected,
|
||||
templateNoteId: templateNoteId
|
||||
});
|
||||
templateNoteId: templateNoteId,
|
||||
promptForType: false,
|
||||
}
|
||||
);
|
||||
} else if (command === "insertChildNote") {
|
||||
const parentNotePath = treeService.getNotePath(this.node);
|
||||
|
||||
noteCreateService.createNote(parentNotePath, {
|
||||
noteCreateService.createNote(
|
||||
{
|
||||
target: "into",
|
||||
parentNoteLink: parentNotePath,
|
||||
type: type,
|
||||
isProtected: this.node.data.isProtected,
|
||||
templateNoteId: templateNoteId
|
||||
});
|
||||
templateNoteId: templateNoteId,
|
||||
promptForType: false,
|
||||
}
|
||||
);
|
||||
} else if (command === "openNoteInSplit") {
|
||||
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
|
||||
const { ntxId } = subContexts?.[subContexts.length - 1] ?? {};
|
||||
|
||||
@ -50,7 +50,7 @@ async function checkNoteAccess(notePath: string, noteContext: NoteContext) {
|
||||
const hoistedNoteId = noteContext.hoistedNoteId;
|
||||
|
||||
if (!resolvedNotePath.includes(hoistedNoteId) && (!resolvedNotePath.includes("_hidden") || resolvedNotePath.includes("_lbBookmarks"))) {
|
||||
const noteId = treeService.getNoteIdFromUrl(resolvedNotePath);
|
||||
const noteId = treeService.getNoteIdFromLink(resolvedNotePath);
|
||||
if (!noteId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -261,7 +261,7 @@ export function parseNavigationStateFromUrl(url: string | undefined) {
|
||||
|
||||
return {
|
||||
notePath,
|
||||
noteId: treeService.getNoteIdFromUrl(notePath),
|
||||
noteId: treeService.getNoteIdFromLink(notePath),
|
||||
ntxId,
|
||||
hoistedNoteId,
|
||||
viewScope,
|
||||
|
||||
@ -5,6 +5,24 @@ import froca from "./froca.js";
|
||||
import { t } from "./i18n.js";
|
||||
import commandRegistry from "./command_registry.js";
|
||||
import type { MentionFeedObjectItem } from "@triliumnext/ckeditor5";
|
||||
import { CreateNoteAction } from "@triliumnext/commons"
|
||||
import FNote from "../entities/fnote.js";
|
||||
|
||||
/**
|
||||
* Extends CKEditor's MentionFeedObjectItem with extra fields used by Trilium.
|
||||
* These additional props (like action, notePath, name, etc.) carry note
|
||||
* metadata and legacy compatibility info needed for custom autocomplete
|
||||
* and link insertion behavior beyond CKEditor’s base mention support.
|
||||
*/
|
||||
type ExtendedMentionFeedObjectItem = MentionFeedObjectItem & {
|
||||
action?: string;
|
||||
noteTitle?: string;
|
||||
name?: string;
|
||||
link?: string;
|
||||
notePath?: string;
|
||||
parentNoteId?: string;
|
||||
highlightedNotePathTitle?: string;
|
||||
};
|
||||
|
||||
// this key needs to have this value, so it's hit by the tooltip
|
||||
const SELECTED_NOTE_PATH_KEY = "data-note-path";
|
||||
@ -23,14 +41,39 @@ function getSearchDelay(notesCount: number): number {
|
||||
}
|
||||
let searchDelay = getSearchDelay(notesCount);
|
||||
|
||||
// TODO: Deduplicate with server.
|
||||
// String values ensure stable, human-readable identifiers across serialization (JSON, CKEditor, logs).
|
||||
export enum SuggestionAction {
|
||||
// These values intentionally mirror CreateNoteAction string values 1:1.
|
||||
// This overlap ensures that when a suggestion triggers a note creation callback,
|
||||
// the receiving features (e.g. note creation handlers, CKEditor mentions) can interpret
|
||||
// the action type consistently
|
||||
CreateNote = CreateNoteAction.CreateNote,
|
||||
CreateChildNote = CreateNoteAction.CreateChildNote,
|
||||
CreateAndLinkNote = CreateNoteAction.CreateAndLinkNote,
|
||||
CreateAndLinkChildNote = CreateNoteAction.CreateAndLinkChildNote,
|
||||
|
||||
SearchNotes = "search-notes",
|
||||
ExternalLink = "external-link",
|
||||
Command = "command",
|
||||
}
|
||||
|
||||
export enum SuggestionMode {
|
||||
SuggestNothing = "nothing",
|
||||
SuggestCreateOnly = "create-only",
|
||||
SuggestCreateAndLink = "create-and-link"
|
||||
}
|
||||
|
||||
// NOTE: Previously marked for deduplication with a server-side type,
|
||||
// but review on 2025-10-12 (using `rg Suggestion`) found no corresponding
|
||||
// server implementation.
|
||||
// This interface appears to be client-only.
|
||||
export interface Suggestion {
|
||||
noteTitle?: string;
|
||||
externalLink?: string;
|
||||
notePathTitle?: string;
|
||||
notePath?: string;
|
||||
highlightedNotePathTitle?: string;
|
||||
action?: string | "create-note" | "search-notes" | "external-link" | "command";
|
||||
action?: SuggestionAction;
|
||||
parentNoteId?: string;
|
||||
icon?: string;
|
||||
commandId?: string;
|
||||
@ -43,7 +86,7 @@ export interface Suggestion {
|
||||
export interface Options {
|
||||
container?: HTMLElement | null;
|
||||
fastSearch?: boolean;
|
||||
allowCreatingNotes?: boolean;
|
||||
suggestionMode?: SuggestionMode;
|
||||
allowJumpToSearchNotes?: boolean;
|
||||
allowExternalLinks?: boolean;
|
||||
/** If set, hides the right-side button corresponding to go to selected note. */
|
||||
@ -54,110 +97,160 @@ export interface Options {
|
||||
isCommandPalette?: boolean;
|
||||
}
|
||||
|
||||
async function autocompleteSourceForCKEditor(queryText: string) {
|
||||
return await new Promise<MentionFeedObjectItem[]>((res, rej) => {
|
||||
async function autocompleteSourceForCKEditor(
|
||||
queryText: string,
|
||||
suggestionMode: SuggestionMode
|
||||
): Promise<MentionFeedObjectItem[]> {
|
||||
// Wrap the callback-based autocompleteSource in a Promise for async/await
|
||||
const rows = await new Promise<Suggestion[]>((resolve) => {
|
||||
autocompleteSource(
|
||||
queryText,
|
||||
(rows) => {
|
||||
res(
|
||||
rows.map((row) => {
|
||||
return {
|
||||
action: row.action,
|
||||
(suggestions) => resolve(suggestions),
|
||||
{
|
||||
suggestionMode,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Map internal suggestions to CKEditor mention feed items
|
||||
return rows.map((row): ExtendedMentionFeedObjectItem => ({
|
||||
action: row.action?.toString(),
|
||||
noteTitle: row.noteTitle,
|
||||
id: `@${row.notePathTitle}`,
|
||||
name: row.notePathTitle || "",
|
||||
link: `#${row.notePath}`,
|
||||
notePath: row.notePath,
|
||||
parentNoteId: row.parentNoteId,
|
||||
highlightedNotePathTitle: row.highlightedNotePathTitle
|
||||
};
|
||||
})
|
||||
);
|
||||
},
|
||||
{
|
||||
allowCreatingNotes: true
|
||||
}
|
||||
);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
async function autocompleteSource(term: string, cb: (rows: Suggestion[]) => void, options: Options = {}) {
|
||||
async function autocompleteSource(
|
||||
term: string,
|
||||
callback: (rows: Suggestion[]) => void,
|
||||
options: Options = {}
|
||||
) {
|
||||
// Check if we're in command mode
|
||||
if (options.isCommandPalette && term.startsWith(">")) {
|
||||
const commandQuery = term.substring(1).trim();
|
||||
|
||||
// Get commands (all if no query, filtered if query provided)
|
||||
const commands = commandQuery.length === 0
|
||||
const commands =
|
||||
commandQuery.length === 0
|
||||
? commandRegistry.getAllCommands()
|
||||
: commandRegistry.searchCommands(commandQuery);
|
||||
|
||||
// Convert commands to suggestions
|
||||
const commandSuggestions: Suggestion[] = commands.map(cmd => ({
|
||||
action: "command",
|
||||
const commandSuggestions: Suggestion[] = commands.map((cmd) => ({
|
||||
action: SuggestionAction.Command,
|
||||
commandId: cmd.id,
|
||||
noteTitle: cmd.name,
|
||||
notePathTitle: `>${cmd.name}`,
|
||||
highlightedNotePathTitle: cmd.name,
|
||||
commandDescription: cmd.description,
|
||||
commandShortcut: cmd.shortcut,
|
||||
icon: cmd.icon
|
||||
icon: cmd.icon,
|
||||
}));
|
||||
|
||||
cb(commandSuggestions);
|
||||
callback(commandSuggestions);
|
||||
return;
|
||||
}
|
||||
|
||||
const fastSearch = options.fastSearch === false ? false : true;
|
||||
if (fastSearch === false) {
|
||||
if (term.trim().length === 0) {
|
||||
return;
|
||||
}
|
||||
cb([
|
||||
const fastSearch = options.fastSearch !== false;
|
||||
const trimmedTerm = term.trim();
|
||||
const activeNoteId = appContext.tabManager.getActiveContextNoteId();
|
||||
|
||||
if (!fastSearch && trimmedTerm.length === 0) return;
|
||||
|
||||
if (!fastSearch) {
|
||||
callback([
|
||||
{
|
||||
noteTitle: term,
|
||||
highlightedNotePathTitle: t("quick-search.searching")
|
||||
}
|
||||
noteTitle: trimmedTerm,
|
||||
highlightedNotePathTitle: t("quick-search.searching"),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
const activeNoteId = appContext.tabManager.getActiveContextNoteId();
|
||||
const length = term.trim().length;
|
||||
|
||||
let results = await server.get<Suggestion[]>(`autocomplete?query=${encodeURIComponent(term)}&activeNoteId=${activeNoteId}&fastSearch=${fastSearch}`);
|
||||
let results = await server.get<Suggestion[]>(
|
||||
`autocomplete?query=${encodeURIComponent(trimmedTerm)}&activeNoteId=${activeNoteId}&fastSearch=${fastSearch}`
|
||||
);
|
||||
|
||||
options.fastSearch = true;
|
||||
|
||||
if (length >= 1 && options.allowCreatingNotes) {
|
||||
// --- Create Note suggestions ---
|
||||
if (trimmedTerm.length >= 1) {
|
||||
switch (options.suggestionMode) {
|
||||
case SuggestionMode.SuggestCreateOnly: {
|
||||
results = [
|
||||
{
|
||||
action: "create-note",
|
||||
noteTitle: term,
|
||||
action: SuggestionAction.CreateNote,
|
||||
noteTitle: trimmedTerm,
|
||||
parentNoteId: "inbox",
|
||||
highlightedNotePathTitle: t("note_autocomplete.create-note", { term: trimmedTerm }),
|
||||
},
|
||||
{
|
||||
action: SuggestionAction.CreateChildNote,
|
||||
noteTitle: trimmedTerm,
|
||||
parentNoteId: activeNoteId || "root",
|
||||
highlightedNotePathTitle: t("note_autocomplete.create-note", { term })
|
||||
} as Suggestion
|
||||
].concat(results);
|
||||
highlightedNotePathTitle: t("note_autocomplete.create-child-note", { term: trimmedTerm }),
|
||||
},
|
||||
...results,
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
if (length >= 1 && options.allowJumpToSearchNotes) {
|
||||
results = results.concat([
|
||||
{
|
||||
action: "search-notes",
|
||||
noteTitle: term,
|
||||
highlightedNotePathTitle: `${t("note_autocomplete.search-for", { term })} <kbd style='color: var(--muted-text-color); background-color: transparent; float: right;'>Ctrl+Enter</kbd>`
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
if (term.match(/^[a-z]+:\/\/.+/i) && options.allowExternalLinks) {
|
||||
case SuggestionMode.SuggestCreateAndLink: {
|
||||
results = [
|
||||
{
|
||||
action: "external-link",
|
||||
externalLink: term,
|
||||
highlightedNotePathTitle: t("note_autocomplete.insert-external-link", { term })
|
||||
} as Suggestion
|
||||
].concat(results);
|
||||
action: SuggestionAction.CreateAndLinkNote,
|
||||
noteTitle: trimmedTerm,
|
||||
parentNoteId: "inbox",
|
||||
highlightedNotePathTitle: t("note_autocomplete.create-and-link-note", { term: trimmedTerm }),
|
||||
},
|
||||
{
|
||||
action: SuggestionAction.CreateAndLinkChildNote,
|
||||
noteTitle: trimmedTerm,
|
||||
parentNoteId: activeNoteId || "root",
|
||||
highlightedNotePathTitle: t("note_autocomplete.create-and-link-child-note", { term: trimmedTerm }),
|
||||
},
|
||||
...results,
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
cb(results);
|
||||
default:
|
||||
// CreateMode.None or undefined → no creation suggestions
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Jump to Search Notes ---
|
||||
if (trimmedTerm.length >= 1 && options.allowJumpToSearchNotes) {
|
||||
results = [
|
||||
...results,
|
||||
{
|
||||
action: SuggestionAction.SearchNotes,
|
||||
noteTitle: trimmedTerm,
|
||||
highlightedNotePathTitle: `${t("note_autocomplete.search-for", {
|
||||
term: trimmedTerm,
|
||||
})} <kbd style='color: var(--muted-text-color); background-color: transparent; float: right;'>Ctrl+Enter</kbd>`,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// --- External Link suggestion ---
|
||||
if (/^[a-z]+:\/\/.+/i.test(trimmedTerm) && options.allowExternalLinks) {
|
||||
results = [
|
||||
{
|
||||
action: SuggestionAction.ExternalLink,
|
||||
externalLink: trimmedTerm,
|
||||
highlightedNotePathTitle: t("note_autocomplete.insert-external-link", { term: trimmedTerm }),
|
||||
},
|
||||
...results,
|
||||
];
|
||||
}
|
||||
|
||||
callback(results);
|
||||
}
|
||||
|
||||
function clearText($el: JQuery<HTMLElement>) {
|
||||
@ -198,6 +291,85 @@ function fullTextSearch($el: JQuery<HTMLElement>, options: Options) {
|
||||
$el.autocomplete("val", searchString);
|
||||
}
|
||||
|
||||
function renderCommandSuggestion(s: Suggestion): string {
|
||||
const icon = s.icon || "bx bx-terminal";
|
||||
const shortcut = s.commandShortcut
|
||||
? `<kbd class="command-shortcut">${s.commandShortcut}</kbd>`
|
||||
: "";
|
||||
|
||||
return `
|
||||
<div class="command-suggestion">
|
||||
<span class="command-icon ${icon}"></span>
|
||||
<div class="command-content">
|
||||
<div class="command-name">${s.highlightedNotePathTitle}</div>
|
||||
${s.commandDescription ? `<div class="command-description">${s.commandDescription}</div>` : ""}
|
||||
</div>
|
||||
${shortcut}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderNoteSuggestion(s: Suggestion): string {
|
||||
const actionClass =
|
||||
s.action === SuggestionAction.SearchNotes ? "search-notes-action" : "";
|
||||
|
||||
const iconClass = (() => {
|
||||
switch (s.action) {
|
||||
case SuggestionAction.SearchNotes:
|
||||
return "bx bx-search";
|
||||
case SuggestionAction.CreateAndLinkNote:
|
||||
case SuggestionAction.CreateNote:
|
||||
return "bx bx-plus";
|
||||
case SuggestionAction.CreateAndLinkChildNote:
|
||||
case SuggestionAction.CreateChildNote:
|
||||
return "bx bx-plus";
|
||||
case SuggestionAction.ExternalLink:
|
||||
return "bx bx-link-external";
|
||||
default:
|
||||
return s.icon ?? "bx bx-note";
|
||||
}
|
||||
})();
|
||||
|
||||
return `
|
||||
<div class="note-suggestion ${actionClass}" style="display:inline-flex; align-items:center;">
|
||||
<span class="icon ${iconClass}" style="display:inline-block; vertical-align:middle; line-height:1; margin-right:0.4em;"></span>
|
||||
<span class="text" style="display:inline-block; vertical-align:middle;">
|
||||
<span class="search-result-title">${s.highlightedNotePathTitle}</span>
|
||||
${s.highlightedAttributeSnippet
|
||||
? `<span class="search-result-attributes">${s.highlightedAttributeSnippet}</span>`
|
||||
: ""}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderSuggestion(suggestion: Suggestion): string {
|
||||
return suggestion.action === SuggestionAction.Command
|
||||
? renderCommandSuggestion(suggestion)
|
||||
: renderNoteSuggestion(suggestion);
|
||||
}
|
||||
|
||||
function mapSuggestionToCreateNoteAction(
|
||||
action: SuggestionAction
|
||||
): CreateNoteAction | null {
|
||||
switch (action) {
|
||||
case SuggestionAction.CreateNote:
|
||||
return CreateNoteAction.CreateNote;
|
||||
|
||||
case SuggestionAction.CreateAndLinkNote:
|
||||
return CreateNoteAction.CreateAndLinkNote;
|
||||
|
||||
case SuggestionAction.CreateChildNote:
|
||||
return CreateNoteAction.CreateChildNote;
|
||||
|
||||
case SuggestionAction.CreateAndLinkChildNote:
|
||||
return CreateNoteAction.CreateAndLinkChildNote;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
|
||||
if ($el.hasClass("note-autocomplete-input")) {
|
||||
// clear any event listener added in previous invocation of this function
|
||||
@ -283,24 +455,21 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
|
||||
$el.autocomplete(
|
||||
{
|
||||
...autocompleteOptions,
|
||||
appendTo: document.querySelector("body"),
|
||||
appendTo: document.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
|
||||
tabAutocomplete: false,
|
||||
},
|
||||
[
|
||||
{
|
||||
source: (term, cb) => {
|
||||
source: (term, callback) => {
|
||||
clearTimeout(debounceTimeoutId);
|
||||
debounceTimeoutId = setTimeout(() => {
|
||||
if (isComposingInput) {
|
||||
return;
|
||||
if (!isComposingInput) {
|
||||
autocompleteSource(term, callback, options);
|
||||
}
|
||||
autocompleteSource(term, cb, options);
|
||||
}, searchDelay);
|
||||
|
||||
if (searchDelay === 0) {
|
||||
@ -308,101 +477,38 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
|
||||
}
|
||||
},
|
||||
displayKey: "notePathTitle",
|
||||
templates: {
|
||||
suggestion: (suggestion) => {
|
||||
if (suggestion.action === "command") {
|
||||
let html = `<div class="command-suggestion">`;
|
||||
html += `<span class="command-icon ${suggestion.icon || "bx bx-terminal"}"></span>`;
|
||||
html += `<div class="command-content">`;
|
||||
html += `<div class="command-name">${suggestion.highlightedNotePathTitle}</div>`;
|
||||
if (suggestion.commandDescription) {
|
||||
html += `<div class="command-description">${suggestion.commandDescription}</div>`;
|
||||
}
|
||||
html += `</div>`;
|
||||
if (suggestion.commandShortcut) {
|
||||
html += `<kbd class="command-shortcut">${suggestion.commandShortcut}</kbd>`;
|
||||
}
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
// Add special class for search-notes action
|
||||
const actionClass = suggestion.action === "search-notes" ? "search-notes-action" : "";
|
||||
|
||||
// Choose appropriate icon based on action
|
||||
let iconClass = suggestion.icon ?? "bx bx-note";
|
||||
if (suggestion.action === "search-notes") {
|
||||
iconClass = "bx bx-search";
|
||||
} else if (suggestion.action === "create-note") {
|
||||
iconClass = "bx bx-plus";
|
||||
} else if (suggestion.action === "external-link") {
|
||||
iconClass = "bx bx-link-external";
|
||||
}
|
||||
|
||||
// Simplified HTML structure without nested divs
|
||||
let html = `<div class="note-suggestion ${actionClass}">`;
|
||||
html += `<span class="icon ${iconClass}"></span>`;
|
||||
html += `<span class="text">`;
|
||||
html += `<span class="search-result-title">${suggestion.highlightedNotePathTitle}</span>`;
|
||||
|
||||
// Add attribute snippet inline if available
|
||||
if (suggestion.highlightedAttributeSnippet) {
|
||||
html += `<span class="search-result-attributes">${suggestion.highlightedAttributeSnippet}</span>`;
|
||||
}
|
||||
|
||||
html += `</span>`;
|
||||
html += `</div>`;
|
||||
return html;
|
||||
}
|
||||
templates: { suggestion: renderSuggestion },
|
||||
cache: false,
|
||||
},
|
||||
// we can't cache identical searches because notes can be created / renamed, new recent notes can be added
|
||||
cache: false
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
// TODO: Types fail due to "autocomplete:selected" not being registered in type definitions.
|
||||
($el as any).on("autocomplete:selected", async (event: Event, suggestion: Suggestion) => {
|
||||
if (suggestion.action === "command") {
|
||||
async function doCommand() {
|
||||
$el.autocomplete("close");
|
||||
$el.trigger("autocomplete:commandselected", [suggestion]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (suggestion.action === "external-link") {
|
||||
async function doExternalLink() {
|
||||
$el.setSelectedNotePath(null);
|
||||
$el.setSelectedExternalLink(suggestion.externalLink);
|
||||
|
||||
$el.autocomplete("val", suggestion.externalLink);
|
||||
|
||||
$el.autocomplete("close");
|
||||
|
||||
$el.trigger("autocomplete:externallinkselected", [suggestion]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (suggestion.action === "create-note") {
|
||||
const { success, noteType, templateNoteId, notePath } = await noteCreateService.chooseNoteType();
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
const { note } = await noteCreateService.createNote( notePath || suggestion.parentNoteId, {
|
||||
title: suggestion.noteTitle,
|
||||
activate: false,
|
||||
type: noteType,
|
||||
templateNoteId: templateNoteId
|
||||
});
|
||||
|
||||
const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||
suggestion.notePath = note?.getBestNotePathString(hoistedNoteId);
|
||||
async function resolveSuggestionNotePathUnderCurrentHoist(note: FNote) {
|
||||
const hoisted = appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||
suggestion.notePath = note.getBestNotePathString(hoisted);
|
||||
}
|
||||
|
||||
if (suggestion.action === "search-notes") {
|
||||
async function doSearchNotes() {
|
||||
const searchString = suggestion.noteTitle;
|
||||
appContext.triggerCommand("searchNotes", { searchString });
|
||||
return;
|
||||
}
|
||||
|
||||
async function selectNoteFromAutocomplete(suggestion: Suggestion) {
|
||||
$el.setSelectedNotePath(suggestion.notePath);
|
||||
$el.setSelectedExternalLink(null);
|
||||
|
||||
@ -411,6 +517,45 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
|
||||
$el.autocomplete("close");
|
||||
|
||||
$el.trigger("autocomplete:noteselected", [suggestion]);
|
||||
}
|
||||
|
||||
switch (suggestion.action) {
|
||||
case SuggestionAction.Command:
|
||||
await doCommand();
|
||||
return;
|
||||
|
||||
case SuggestionAction.ExternalLink:
|
||||
await doExternalLink();
|
||||
break;
|
||||
|
||||
case SuggestionAction.CreateNote:
|
||||
case SuggestionAction.CreateAndLinkNote:
|
||||
case SuggestionAction.CreateChildNote:
|
||||
case SuggestionAction.CreateAndLinkChildNote: {
|
||||
const createNoteAction = mapSuggestionToCreateNoteAction(
|
||||
suggestion.action
|
||||
)!;
|
||||
const { note } = await noteCreateService.createNoteFromAction(
|
||||
createNoteAction,
|
||||
true,
|
||||
suggestion.noteTitle,
|
||||
suggestion.parentNoteId,
|
||||
);
|
||||
|
||||
if (!note) break;
|
||||
|
||||
await resolveSuggestionNotePathUnderCurrentHoist(note);
|
||||
await selectNoteFromAutocomplete(suggestion);
|
||||
break;
|
||||
}
|
||||
|
||||
case SuggestionAction.SearchNotes:
|
||||
await doSearchNotes();
|
||||
break;
|
||||
|
||||
default:
|
||||
await selectNoteFromAutocomplete(suggestion);
|
||||
}
|
||||
});
|
||||
|
||||
$el.on("autocomplete:closed", () => {
|
||||
|
||||
@ -10,8 +10,63 @@ import type FNote from "../entities/fnote.js";
|
||||
import type FBranch from "../entities/fbranch.js";
|
||||
import type { ChooseNoteTypeResponse } from "../widgets/dialogs/note_type_chooser.js";
|
||||
import type { CKTextEditor } from "@triliumnext/ckeditor5";
|
||||
import dateNoteService from "../services/date_notes.js";
|
||||
import { CreateNoteAction } from "@triliumnext/commons";
|
||||
|
||||
export interface CreateNoteOpts {
|
||||
/**
|
||||
* Defines the type hierarchy and rules for valid argument combinations
|
||||
* accepted by `note_create`.
|
||||
*
|
||||
* ## Overview
|
||||
* Each variant extends `CreateNoteOpts` and enforces specific constraints to
|
||||
* ensure only valid note creation options are allowed at compile time.
|
||||
*
|
||||
* ## Type Safety
|
||||
* The `PromptingRule` ensures that `promptForType` and `type` stay mutually
|
||||
* exclusive (if prompting, `type` is undefined).
|
||||
*
|
||||
* The type system prevents invalid argument mixes by design — successful type
|
||||
* checks guarantee a valid state, following Curry–Howard correspondence
|
||||
* principles (types as proofs).
|
||||
*
|
||||
* ## Maintenance
|
||||
* If adding or modifying `Opts`, ensure:
|
||||
* - All valid combinations are represented (avoid *false negatives*).
|
||||
* - No invalid ones slip through (avoid *false positives*).
|
||||
*
|
||||
* Hierarchy (general → specific):
|
||||
* - CreateNoteOpts
|
||||
* - CreateNoteWithUrlOpts
|
||||
* - CreateNoteIntoDefaultOpts
|
||||
*/
|
||||
|
||||
/** enforces a truth rule:
|
||||
* - If `promptForType` is true → `type` must be undefined.
|
||||
* - If `promptForType` is false → `type` must be defined.
|
||||
*/
|
||||
type PromptingRule = {
|
||||
promptForType: true;
|
||||
type?: never;
|
||||
} | {
|
||||
promptForType?: false;
|
||||
/**
|
||||
* The note type (e.g. "text", "code", "image", "mermaid", etc.).
|
||||
*
|
||||
* If omitted, the server will automatically default to `"text"`.
|
||||
* TypeScript still enforces explicit typing unless `promptForType` is true,
|
||||
* to encourage clarity at the call site.
|
||||
*/
|
||||
type?: string;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Base type for all note creation options (domain hypernym).
|
||||
* All specific note option types extend from this.
|
||||
*
|
||||
* Combine with `&` to ensure valid logical combinations.
|
||||
*/
|
||||
type CreateNoteBase = {
|
||||
isProtected?: boolean;
|
||||
saveSelection?: boolean;
|
||||
title?: string | null;
|
||||
@ -21,10 +76,34 @@ export interface CreateNoteOpts {
|
||||
templateNoteId?: string;
|
||||
activate?: boolean;
|
||||
focus?: "title" | "content";
|
||||
target?: string;
|
||||
targetBranchId?: string;
|
||||
textEditor?: CKTextEditor;
|
||||
}
|
||||
} & PromptingRule;
|
||||
|
||||
/*
|
||||
* Defines options for creating a note at a specific path.
|
||||
* Serves as a base for "into", "before", and "after" variants,
|
||||
* sharing common URL-related fields.
|
||||
*/
|
||||
export type CreateNoteWithLinkOpts =
|
||||
| (CreateNoteBase & {
|
||||
target: "into";
|
||||
parentNoteLink?: string;
|
||||
// No branch ID needed for "into"
|
||||
})
|
||||
| (CreateNoteBase & {
|
||||
target: "before" | "after";
|
||||
// Either an Url or a Path
|
||||
parentNoteLink?: string;
|
||||
// Required for "before"/"after"
|
||||
targetBranchId: string;
|
||||
});
|
||||
|
||||
export type CreateNoteIntoDefaultOpts = CreateNoteBase & {
|
||||
target: "default";
|
||||
parentNoteLink?: never;
|
||||
};
|
||||
|
||||
export type CreateNoteOpts = CreateNoteWithLinkOpts | CreateNoteIntoDefaultOpts;
|
||||
|
||||
interface Response {
|
||||
// TODO: Deduplicate with server once we have client/server architecture.
|
||||
@ -37,7 +116,141 @@ interface DuplicateResponse {
|
||||
note: FNote;
|
||||
}
|
||||
|
||||
async function createNote(parentNotePath: string | undefined, options: CreateNoteOpts = {}) {
|
||||
// The low level note creation
|
||||
async function createNote(
|
||||
options: CreateNoteOpts
|
||||
): Promise<{ note: FNote | null; branch: FBranch | undefined }> {
|
||||
|
||||
let resolvedOptions = { ...options };
|
||||
|
||||
// handle prompts centrally to write once fix for all
|
||||
if (options.promptForType) {
|
||||
const maybeResolvedOptions = await promptForType(options);
|
||||
if (!maybeResolvedOptions) {
|
||||
return { note: null, branch: undefined };
|
||||
}
|
||||
|
||||
resolvedOptions = maybeResolvedOptions;
|
||||
}
|
||||
|
||||
|
||||
switch(resolvedOptions.target) {
|
||||
case "default":
|
||||
return createNoteIntoDefaultLocation(resolvedOptions);
|
||||
case "into":
|
||||
case "before":
|
||||
case "after":
|
||||
return createNoteWithLink(resolvedOptions);
|
||||
}
|
||||
}
|
||||
|
||||
// A wrapper to standardize note creation
|
||||
async function createNoteFromAction(
|
||||
action: CreateNoteAction,
|
||||
promptForType: boolean,
|
||||
title: string | undefined,
|
||||
parentNoteLink: string | undefined,
|
||||
): Promise<{ note: FNote | null; branch: FBranch | undefined }> {
|
||||
switch (action) {
|
||||
case CreateNoteAction.CreateNote: {
|
||||
const resp = await createNote(
|
||||
{
|
||||
target: "default",
|
||||
title: title,
|
||||
activate: true,
|
||||
promptForType,
|
||||
}
|
||||
);
|
||||
return resp;
|
||||
}
|
||||
case CreateNoteAction.CreateAndLinkNote: {
|
||||
const resp = await createNote(
|
||||
{
|
||||
target: "default",
|
||||
title,
|
||||
activate: false,
|
||||
promptForType,
|
||||
}
|
||||
);
|
||||
return resp;
|
||||
}
|
||||
case CreateNoteAction.CreateChildNote: {
|
||||
if (!parentNoteLink) {
|
||||
console.warn("createNoteFromAction: Missing parentNoteLink");
|
||||
return { note: null, branch: undefined };
|
||||
}
|
||||
|
||||
const resp = await createNote(
|
||||
{
|
||||
target: "into",
|
||||
parentNoteLink,
|
||||
title,
|
||||
activate: true,
|
||||
promptForType,
|
||||
},
|
||||
);
|
||||
return resp
|
||||
}
|
||||
case CreateNoteAction.CreateAndLinkChildNote: {
|
||||
if (!parentNoteLink) {
|
||||
console.warn("createNoteFromAction: Missing parentNoteLink");
|
||||
return { note: null, branch: undefined };
|
||||
}
|
||||
const resp = await createNote(
|
||||
{
|
||||
target: "into",
|
||||
parentNoteLink: parentNoteLink,
|
||||
title,
|
||||
activate: false,
|
||||
promptForType,
|
||||
},
|
||||
)
|
||||
return resp;
|
||||
}
|
||||
|
||||
default:
|
||||
console.warn("Unknown CreateNoteAction:", action);
|
||||
return { note: null, branch: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
async function promptForType(
|
||||
options: CreateNoteOpts
|
||||
) : Promise<CreateNoteOpts | null> {
|
||||
const { success, noteType, templateNoteId, notePath } = await chooseNoteType();
|
||||
|
||||
if (!success) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let resolvedOptions: CreateNoteOpts = {
|
||||
...options,
|
||||
promptForType: false,
|
||||
type: noteType,
|
||||
templateNoteId,
|
||||
};
|
||||
|
||||
if (notePath) {
|
||||
resolvedOptions = {
|
||||
...resolvedOptions,
|
||||
target: "into",
|
||||
parentNoteLink: notePath,
|
||||
};
|
||||
}
|
||||
|
||||
return resolvedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new note under a specified parent note path.
|
||||
*
|
||||
* @param target - Mirrors the `createNote` API in apps/server/src/routes/api/notes.ts.
|
||||
* @param options - Note creation options
|
||||
* @returns A promise resolving with the created note and its branch.
|
||||
*/
|
||||
async function createNoteWithLink(
|
||||
options: CreateNoteWithLinkOpts
|
||||
): Promise<{ note: FNote | null; branch: FBranch | undefined }> {
|
||||
options = Object.assign(
|
||||
{
|
||||
activate: true,
|
||||
@ -61,7 +274,8 @@ async function createNote(parentNotePath: string | undefined, options: CreateNot
|
||||
[options.title, options.content] = parseSelectedHtml(options.textEditor.getSelectedHtml());
|
||||
}
|
||||
|
||||
const parentNoteId = treeService.getNoteIdFromUrl(parentNotePath);
|
||||
const parentNoteLink = options.parentNoteLink;
|
||||
const parentNoteId = treeService.getNoteIdFromLink(parentNoteLink);
|
||||
|
||||
if (options.type === "mermaid" && !options.content && !options.templateNoteId) {
|
||||
options.content = `graph TD;
|
||||
@ -71,7 +285,12 @@ async function createNote(parentNotePath: string | undefined, options: CreateNot
|
||||
C-->D;`;
|
||||
}
|
||||
|
||||
const { note, branch } = await server.post<Response>(`notes/${parentNoteId}/children?target=${options.target}&targetBranchId=${options.targetBranchId || ""}`, {
|
||||
const query =
|
||||
options.target === "into"
|
||||
? `target=${options.target}`
|
||||
: `target=${options.target}&targetBranchId=${options.targetBranchId}`;
|
||||
|
||||
const { note, branch } = await server.post<Response>(`notes/${parentNoteId}/children?${query}`, {
|
||||
title: options.title,
|
||||
content: options.content || "",
|
||||
isProtected: options.isProtected,
|
||||
@ -89,7 +308,7 @@ async function createNote(parentNotePath: string | undefined, options: CreateNot
|
||||
|
||||
const activeNoteContext = appContext.tabManager.getActiveContext();
|
||||
if (activeNoteContext && options.activate) {
|
||||
await activeNoteContext.setNote(`${parentNotePath}/${note.noteId}`);
|
||||
await activeNoteContext.setNote(`${parentNoteId}/${note.noteId}`);
|
||||
|
||||
if (options.focus === "title") {
|
||||
appContext.triggerEvent("focusAndSelectTitle", { isNewNote: true });
|
||||
@ -107,25 +326,46 @@ async function createNote(parentNotePath: string | undefined, options: CreateNot
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new note inside the user's Inbox.
|
||||
*
|
||||
* @param {CreateNoteIntoDefaultOpts} [options] - Optional settings such as title, type, template, or content.
|
||||
* @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.
|
||||
*/
|
||||
async function createNoteIntoDefaultLocation(
|
||||
options: CreateNoteIntoDefaultOpts
|
||||
): Promise<{ note: FNote | null; branch: FBranch | undefined }> {
|
||||
const inboxNote = await dateNoteService.getInboxNote();
|
||||
if (!inboxNote) {
|
||||
console.warn("Missing inbox note.");
|
||||
// always return a defined object
|
||||
return { note: null, branch: undefined };
|
||||
}
|
||||
|
||||
if (options.isProtected === undefined) {
|
||||
options.isProtected =
|
||||
inboxNote.isProtected && protectedSessionHolder.isProtectedSessionAvailable();
|
||||
}
|
||||
|
||||
const result = await createNoteWithLink(
|
||||
{
|
||||
...options,
|
||||
target: "into",
|
||||
parentNoteLink: inboxNote.getBestNotePathString(),
|
||||
}
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function chooseNoteType() {
|
||||
return new Promise<ChooseNoteTypeResponse>((res) => {
|
||||
appContext.triggerCommand("chooseNoteType", { callback: res });
|
||||
});
|
||||
}
|
||||
|
||||
async function createNoteWithTypePrompt(parentNotePath: string, options: CreateNoteOpts = {}) {
|
||||
const { success, noteType, templateNoteId, notePath } = await chooseNoteType();
|
||||
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
options.type = noteType;
|
||||
options.templateNoteId = templateNoteId;
|
||||
|
||||
return await createNote(notePath || parentNotePath, options);
|
||||
}
|
||||
|
||||
/* If the first element is heading, parse it out and use it as a new heading. */
|
||||
function parseSelectedHtml(selectedHtml: string) {
|
||||
const dom = $.parseHTML(selectedHtml);
|
||||
@ -146,7 +386,7 @@ function parseSelectedHtml(selectedHtml: string) {
|
||||
}
|
||||
|
||||
async function duplicateSubtree(noteId: string, parentNotePath: string) {
|
||||
const parentNoteId = treeService.getNoteIdFromUrl(parentNotePath);
|
||||
const parentNoteId = treeService.getNoteIdFromLink(parentNotePath);
|
||||
const { note } = await server.post<DuplicateResponse>(`notes/${noteId}/duplicate/${parentNoteId}`);
|
||||
|
||||
await ws.waitForMaxKnownEntityChangeId();
|
||||
@ -159,7 +399,6 @@ async function duplicateSubtree(noteId: string, parentNotePath: string) {
|
||||
|
||||
export default {
|
||||
createNote,
|
||||
createNoteWithTypePrompt,
|
||||
createNoteFromAction,
|
||||
duplicateSubtree,
|
||||
chooseNoteType
|
||||
};
|
||||
|
||||
@ -92,7 +92,7 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root
|
||||
if (effectivePathSegments.includes(hoistedNoteId) && effectivePathSegments.includes('root')) {
|
||||
return effectivePathSegments;
|
||||
} else {
|
||||
const noteId = getNoteIdFromUrl(notePath);
|
||||
const noteId = getNoteIdFromLink(notePath);
|
||||
if (!noteId) {
|
||||
throw new Error(`Unable to find note with ID: ${noteId}.`);
|
||||
}
|
||||
@ -129,7 +129,7 @@ function getParentProtectedStatus(node: Fancytree.FancytreeNode) {
|
||||
return hoistedNoteService.isHoistedNode(node) ? false : node.getParent().data.isProtected;
|
||||
}
|
||||
|
||||
function getNoteIdFromUrl(urlOrNotePath: string | null | undefined) {
|
||||
function getNoteIdFromLink(urlOrNotePath: string | null | undefined) {
|
||||
if (!urlOrNotePath) {
|
||||
return null;
|
||||
}
|
||||
@ -306,7 +306,7 @@ export default {
|
||||
getParentProtectedStatus,
|
||||
getNotePath,
|
||||
getNotePathTitleComponents,
|
||||
getNoteIdFromUrl,
|
||||
getNoteIdFromLink,
|
||||
getNoteIdAndParentIdFromUrl,
|
||||
getBranchIdFromUrl,
|
||||
getNoteTitle,
|
||||
|
||||
@ -1897,7 +1897,10 @@
|
||||
},
|
||||
"note_autocomplete": {
|
||||
"search-for": "Search for \"{{term}}\"",
|
||||
"create-note": "Create and link child note \"{{term}}\"",
|
||||
"create-child-note": "Create child note \"{{term}}\"",
|
||||
"create-note": "Create note \"{{term}}\"",
|
||||
"create-and-link-child-note": "Create and link child note \"{{term}}\"",
|
||||
"create-and-link-note": "Create and link note \"{{term}}\"",
|
||||
"insert-external-link": "Insert external link to \"{{term}}\"",
|
||||
"clear-text-field": "Clear text field",
|
||||
"show-recent-notes": "Show recent notes",
|
||||
|
||||
@ -3,7 +3,7 @@ import server from "../../services/server.js";
|
||||
import froca from "../../services/froca.js";
|
||||
import linkService from "../../services/link.js";
|
||||
import attributeAutocompleteService from "../../services/attribute_autocomplete.js";
|
||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||
import noteAutocompleteService, { SuggestionMode } from "../../services/note_autocomplete.js";
|
||||
import promotedAttributeDefinitionParser from "../../services/promoted_attribute_definition_parser.js";
|
||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||
import SpacedUpdate from "../../services/spaced_update.js";
|
||||
@ -429,7 +429,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
||||
this.$rowTargetNote = this.$widget.find(".attr-row-target-note");
|
||||
this.$inputTargetNote = this.$widget.find(".attr-input-target-note");
|
||||
|
||||
noteAutocompleteService.initNoteAutocomplete(this.$inputTargetNote, { allowCreatingNotes: true }).on("autocomplete:noteselected", (event, suggestion, dataset) => {
|
||||
noteAutocompleteService.initNoteAutocomplete(this.$inputTargetNote, { suggestionMode: SuggestionMode.SuggestCreateAndLink }).on("autocomplete:noteselected", (event, suggestion, dataset) => {
|
||||
if (!suggestion.notePath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import branches from "../../../services/branches";
|
||||
import { executeBulkActions } from "../../../services/bulk_action";
|
||||
import froca from "../../../services/froca";
|
||||
import { t } from "../../../services/i18n";
|
||||
import note_create from "../../../services/note_create";
|
||||
import note_create from "../../../services/note_create.js";
|
||||
import server from "../../../services/server";
|
||||
import { ColumnMap } from "./data";
|
||||
|
||||
@ -39,9 +39,11 @@ export default class BoardApi {
|
||||
const parentNotePath = this.parentNote.noteId;
|
||||
|
||||
// Create a new note as a child of the parent note
|
||||
const { note: newNote, branch: newBranch } = await note_create.createNote(parentNotePath, {
|
||||
const { note: newNote, branch: newBranch } = await note_create.createNote({
|
||||
target: "into",
|
||||
parentNoteLink: parentNotePath,
|
||||
activate: false,
|
||||
title
|
||||
title,
|
||||
});
|
||||
|
||||
if (newNote && newBranch) {
|
||||
@ -139,13 +141,17 @@ export default class BoardApi {
|
||||
async insertRowAtPosition(
|
||||
column: string,
|
||||
relativeToBranchId: string,
|
||||
direction: "before" | "after") {
|
||||
const { note, branch } = await note_create.createNote(this.parentNote.noteId, {
|
||||
direction: "before" | "after"
|
||||
) {
|
||||
const { note, branch } = await note_create.createNote(
|
||||
{
|
||||
target: direction,
|
||||
parentNoteLink: this.parentNote.noteId,
|
||||
activate: false,
|
||||
targetBranchId: relativeToBranchId,
|
||||
target: direction,
|
||||
title: t("board_view.new-item")
|
||||
});
|
||||
title: t("board_view.new-item"),
|
||||
}
|
||||
);
|
||||
|
||||
if (!note || !branch) {
|
||||
throw new Error("Failed to create note");
|
||||
|
||||
@ -57,12 +57,18 @@ export function openNoteContextMenu(api: Api, event: ContextMenuEvent, note: FNo
|
||||
{
|
||||
title: t("board_view.insert-above"),
|
||||
uiIcon: "bx bx-list-plus",
|
||||
handler: () => api.insertRowAtPosition(column, branchId, "before")
|
||||
handler: () => api.insertRowAtPosition(
|
||||
column,
|
||||
branchId,
|
||||
"before")
|
||||
},
|
||||
{
|
||||
title: t("board_view.insert-below"),
|
||||
uiIcon: "bx bx-empty",
|
||||
handler: () => api.insertRowAtPosition(column, branchId, "after")
|
||||
handler: () => api.insertRowAtPosition(
|
||||
column,
|
||||
branchId,
|
||||
"after")
|
||||
},
|
||||
{ kind: "separator" },
|
||||
{
|
||||
|
||||
@ -15,6 +15,7 @@ import FormTextArea from "../../react/FormTextArea";
|
||||
import FNote from "../../../entities/fnote";
|
||||
import NoteAutocomplete from "../../react/NoteAutocomplete";
|
||||
import toast from "../../../services/toast";
|
||||
import { SuggestionMode } from "../../../services/note_autocomplete";
|
||||
|
||||
export interface BoardViewData {
|
||||
columns?: BoardColumnData[];
|
||||
@ -309,7 +310,7 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, mode, is
|
||||
noteId={currentValue ?? ""}
|
||||
opts={{
|
||||
hideAllButtons: true,
|
||||
allowCreatingNotes: true
|
||||
suggestionMode: SuggestionMode.SuggestCreateAndLink
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Escape") {
|
||||
|
||||
@ -6,6 +6,7 @@ import Icon from "../../react/Icon.jsx";
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
import froca from "../../../services/froca.js";
|
||||
import NoteAutocomplete from "../../react/NoteAutocomplete.jsx";
|
||||
import { SuggestionMode } from "../../../services/note_autocomplete.js";
|
||||
|
||||
type ColumnType = LabelType | "relation";
|
||||
|
||||
@ -227,7 +228,7 @@ function RelationEditor({ cell, success }: EditorOpts) {
|
||||
inputRef={inputRef}
|
||||
noteId={cell.getValue()}
|
||||
opts={{
|
||||
allowCreatingNotes: true,
|
||||
suggestionMode: SuggestionMode.SuggestCreateAndLink,
|
||||
hideAllButtons: true
|
||||
}}
|
||||
noteIdChanged={success}
|
||||
|
||||
@ -181,8 +181,8 @@ export function showRowContextMenu(parentComponent: Component, e: MouseEvent, ro
|
||||
uiIcon: "bx bx-horizontal-left bx-rotate-90",
|
||||
enabled: !sorters.length,
|
||||
handler: () => parentComponent?.triggerCommand("addNewRow", {
|
||||
parentNotePath: parentNoteId,
|
||||
customOpts: {
|
||||
parentNoteLink: parentNoteId,
|
||||
target: "before",
|
||||
targetBranchId: rowData.branchId,
|
||||
}
|
||||
@ -194,9 +194,12 @@ export function showRowContextMenu(parentComponent: Component, e: MouseEvent, ro
|
||||
handler: async () => {
|
||||
const branchId = row.getData().branchId;
|
||||
const note = await froca.getBranch(branchId)?.getNote();
|
||||
if (!note) {
|
||||
return;
|
||||
}
|
||||
parentComponent?.triggerCommand("addNewRow", {
|
||||
parentNotePath: note?.noteId,
|
||||
customOpts: {
|
||||
parentNoteLink: note.noteId,
|
||||
target: "after",
|
||||
targetBranchId: branchId,
|
||||
}
|
||||
@ -208,8 +211,8 @@ export function showRowContextMenu(parentComponent: Component, e: MouseEvent, ro
|
||||
uiIcon: "bx bx-horizontal-left bx-rotate-270",
|
||||
enabled: !sorters.length,
|
||||
handler: () => parentComponent?.triggerCommand("addNewRow", {
|
||||
parentNotePath: parentNoteId,
|
||||
customOpts: {
|
||||
parentNoteLink: parentNoteId,
|
||||
target: "after",
|
||||
targetBranchId: rowData.branchId,
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { EventCallBackMethods, RowComponent, Tabulator } from "tabulator-tables";
|
||||
import { CommandListenerData } from "../../../components/app_context";
|
||||
import note_create, { CreateNoteOpts } from "../../../services/note_create";
|
||||
import note_create from "../../../services/note_create";
|
||||
import { useLegacyImperativeHandlers } from "../../react/hooks";
|
||||
import { RefObject } from "preact";
|
||||
import { setAttribute, setLabel } from "../../../services/attributes";
|
||||
@ -9,17 +9,23 @@ import server from "../../../services/server";
|
||||
import branches from "../../../services/branches";
|
||||
import AttributeDetailWidget from "../../attribute_widgets/attribute_detail";
|
||||
|
||||
/**
|
||||
* Hook for handling row table editing, including adding new rows.
|
||||
*/
|
||||
export default function useRowTableEditing(api: RefObject<Tabulator>, attributeDetailWidget: AttributeDetailWidget, parentNotePath: string): Partial<EventCallBackMethods> {
|
||||
// Adding new rows
|
||||
useLegacyImperativeHandlers({
|
||||
addNewRowCommand({ customOpts, parentNotePath: customNotePath }: CommandListenerData<"addNewRow">) {
|
||||
const notePath = customNotePath ?? parentNotePath;
|
||||
if (notePath) {
|
||||
const opts: CreateNoteOpts = {
|
||||
activate: false,
|
||||
...customOpts
|
||||
addNewRowCommand({ customOpts }: CommandListenerData<"addNewRow">) {
|
||||
if (!customOpts) {
|
||||
customOpts = {
|
||||
target: "into",
|
||||
};
|
||||
}
|
||||
note_create.createNote(notePath, opts).then(({ branch }) => {
|
||||
|
||||
const noteUrl = customOpts.parentNoteLink ?? parentNotePath;
|
||||
if (noteUrl) {
|
||||
customOpts.parentNoteLink = noteUrl;
|
||||
customOpts.activate = false;
|
||||
note_create.createNote(customOpts).then(({ branch }) => {
|
||||
if (branch) {
|
||||
setTimeout(() => {
|
||||
if (!api.current) return;
|
||||
@ -27,6 +33,7 @@ export default function useRowTableEditing(api: RefObject<Tabulator>, attributeD
|
||||
}, 100);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -5,7 +5,7 @@ import FormRadioGroup from "../react/FormRadioGroup";
|
||||
import NoteAutocomplete from "../react/NoteAutocomplete";
|
||||
import { useRef, useState, useEffect } from "preact/hooks";
|
||||
import tree from "../../services/tree";
|
||||
import note_autocomplete, { Suggestion } from "../../services/note_autocomplete";
|
||||
import note_autocomplete, { SuggestionMode, Suggestion } from "../../services/note_autocomplete";
|
||||
import { logError } from "../../services/ws";
|
||||
import FormGroup from "../react/FormGroup.js";
|
||||
import { refToJQuerySelector } from "../react/react_utils";
|
||||
@ -58,7 +58,7 @@ export default function AddLinkDialog() {
|
||||
}
|
||||
|
||||
if (suggestion.notePath) {
|
||||
const noteId = tree.getNoteIdFromUrl(suggestion.notePath);
|
||||
const noteId = tree.getNoteIdFromLink(suggestion.notePath);
|
||||
if (noteId) {
|
||||
setDefaultLinkTitle(noteId);
|
||||
}
|
||||
@ -133,7 +133,7 @@ export default function AddLinkDialog() {
|
||||
onChange={setSuggestion}
|
||||
opts={{
|
||||
allowExternalLinks: true,
|
||||
allowCreatingNotes: true
|
||||
suggestionMode: SuggestionMode.SuggestCreateAndLink,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
@ -5,7 +5,7 @@ import FormRadioGroup from "../react/FormRadioGroup";
|
||||
import Modal from "../react/Modal";
|
||||
import NoteAutocomplete from "../react/NoteAutocomplete";
|
||||
import Button from "../react/Button";
|
||||
import { Suggestion, triggerRecentNotes } from "../../services/note_autocomplete";
|
||||
import { SuggestionMode, Suggestion, triggerRecentNotes } from "../../services/note_autocomplete.js";
|
||||
import tree from "../../services/tree";
|
||||
import froca from "../../services/froca";
|
||||
import { useTriliumEvent } from "../react/hooks";
|
||||
@ -50,7 +50,7 @@ export default function IncludeNoteDialog() {
|
||||
inputRef={autoCompleteRef}
|
||||
opts={{
|
||||
hideGoToSelectedNoteButton: true,
|
||||
allowCreatingNotes: true
|
||||
suggestionMode: SuggestionMode.SuggestCreateOnly,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
@ -71,7 +71,7 @@ export default function IncludeNoteDialog() {
|
||||
}
|
||||
|
||||
async function includeNote(notePath: string, editorApi: CKEditorApi, boxSize: BoxSize) {
|
||||
const noteId = tree.getNoteIdFromUrl(notePath);
|
||||
const noteId = tree.getNoteIdFromLink(notePath);
|
||||
if (!noteId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import Button from "../react/Button";
|
||||
import NoteAutocomplete from "../react/NoteAutocomplete";
|
||||
import { t } from "../../services/i18n";
|
||||
import { useRef, useState } from "preact/hooks";
|
||||
import note_autocomplete, { Suggestion } from "../../services/note_autocomplete";
|
||||
import note_autocomplete, { SuggestionMode, Suggestion } from "../../services/note_autocomplete.js";
|
||||
import appContext from "../../components/app_context";
|
||||
import commandRegistry from "../../services/command_registry";
|
||||
import { refToJQuerySelector } from "../react/react_utils";
|
||||
@ -12,34 +12,53 @@ import shortcutService from "../../services/shortcuts";
|
||||
|
||||
const KEEP_LAST_SEARCH_FOR_X_SECONDS = 120;
|
||||
|
||||
type Mode = "last-search" | "recent-notes" | "commands";
|
||||
enum Mode {
|
||||
LastSearch,
|
||||
RecentNotes,
|
||||
Commands,
|
||||
}
|
||||
|
||||
export default function JumpToNoteDialogComponent() {
|
||||
const [ mode, setMode ] = useState<Mode>();
|
||||
const [ lastOpenedTs, setLastOpenedTs ] = useState<number>(0);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const autocompleteRef = useRef<HTMLInputElement>(null);
|
||||
const [ isCommandMode, setIsCommandMode ] = useState(mode === "commands");
|
||||
const [ isCommandMode, setIsCommandMode ] = useState(mode === Mode.Commands);
|
||||
const [ initialText, setInitialText ] = useState(isCommandMode ? "> " : "");
|
||||
const actualText = useRef<string>(initialText);
|
||||
const [ shown, setShown ] = useState(false);
|
||||
|
||||
async function openDialog(commandMode: boolean) {
|
||||
async function openDialog(requestedMode: Mode) {
|
||||
let newMode: Mode;
|
||||
let initialText = "";
|
||||
|
||||
if (commandMode) {
|
||||
newMode = "commands";
|
||||
switch (requestedMode) {
|
||||
case Mode.Commands:
|
||||
newMode = Mode.Commands;
|
||||
initialText = ">";
|
||||
} else if (Date.now() - lastOpenedTs <= KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000 && actualText.current) {
|
||||
break;
|
||||
|
||||
case Mode.LastSearch:
|
||||
// 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.
|
||||
newMode = "last-search";
|
||||
if (Date.now() - lastOpenedTs <= KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000 && actualText.current) {
|
||||
newMode = Mode.LastSearch;
|
||||
initialText = actualText.current;
|
||||
} else {
|
||||
newMode = "recent-notes";
|
||||
newMode = Mode.RecentNotes;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (Date.now() - lastOpenedTs <= KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000 && actualText.current) {
|
||||
newMode = Mode.LastSearch;
|
||||
initialText = actualText.current;
|
||||
} else {
|
||||
newMode = Mode.RecentNotes;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode !== newMode) {
|
||||
@ -51,8 +70,8 @@ export default function JumpToNoteDialogComponent() {
|
||||
setLastOpenedTs(Date.now());
|
||||
}
|
||||
|
||||
useTriliumEvent("jumpToNote", () => openDialog(false));
|
||||
useTriliumEvent("commandPalette", () => openDialog(true));
|
||||
useTriliumEvent("jumpToNote", () => openDialog(Mode.RecentNotes));
|
||||
useTriliumEvent("commandPalette", () => openDialog(Mode.Commands));
|
||||
|
||||
async function onItemSelected(suggestion?: Suggestion | null) {
|
||||
if (!suggestion) {
|
||||
@ -70,12 +89,12 @@ export default function JumpToNoteDialogComponent() {
|
||||
function onShown() {
|
||||
const $autoComplete = refToJQuerySelector(autocompleteRef);
|
||||
switch (mode) {
|
||||
case "last-search":
|
||||
case Mode.LastSearch:
|
||||
break;
|
||||
case "recent-notes":
|
||||
case Mode.RecentNotes:
|
||||
note_autocomplete.showRecentNotes($autoComplete);
|
||||
break;
|
||||
case "commands":
|
||||
case Mode.Commands:
|
||||
note_autocomplete.showAllCommands($autoComplete);
|
||||
break;
|
||||
}
|
||||
@ -116,7 +135,7 @@ export default function JumpToNoteDialogComponent() {
|
||||
container={containerRef}
|
||||
text={initialText}
|
||||
opts={{
|
||||
allowCreatingNotes: true,
|
||||
suggestionMode: SuggestionMode.SuggestCreateOnly,
|
||||
hideGoToSelectedNoteButton: true,
|
||||
allowJumpToSearchNotes: true,
|
||||
isCommandPalette: true
|
||||
|
||||
@ -7,7 +7,7 @@ import { useEffect, useState } from "preact/hooks";
|
||||
import note_types from "../../services/note_types";
|
||||
import { MenuCommandItem, MenuItem } from "../../menus/context_menu";
|
||||
import { TreeCommandNames } from "../../menus/tree_context_menu";
|
||||
import { Suggestion } from "../../services/note_autocomplete";
|
||||
import { SuggestionMode, Suggestion } from "../../services/note_autocomplete.js";
|
||||
import Badge from "../react/Badge";
|
||||
import { useTriliumEvent } from "../react/hooks";
|
||||
|
||||
@ -76,6 +76,7 @@ export default function NoteTypeChooserDialogComponent() {
|
||||
onHidden={() => {
|
||||
callback?.({ success: false });
|
||||
setShown(false);
|
||||
setParentNote(null);
|
||||
}}
|
||||
show={shown}
|
||||
stackable
|
||||
@ -85,7 +86,7 @@ export default function NoteTypeChooserDialogComponent() {
|
||||
onChange={setParentNote}
|
||||
placeholder={t("note_type_chooser.search_placeholder")}
|
||||
opts={{
|
||||
allowCreatingNotes: false,
|
||||
suggestionMode: SuggestionMode.SuggestNothing,
|
||||
hideGoToSelectedNoteButton: true,
|
||||
allowJumpToSearchNotes: false,
|
||||
}}
|
||||
|
||||
@ -5,7 +5,7 @@ import BasicWidget from "../basic_widget.js";
|
||||
import toastService from "../../services/toast.js";
|
||||
import appContext from "../../components/app_context.js";
|
||||
import server from "../../services/server.js";
|
||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||
import noteAutocompleteService, { SuggestionMode } from "../../services/note_autocomplete.js";
|
||||
|
||||
import { TPL, addMessageToChat, showSources, hideSources, showLoadingIndicator, hideLoadingIndicator } from "./ui.js";
|
||||
import { formatMarkdown } from "./utils.js";
|
||||
@ -163,7 +163,7 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
const mentionSetup: MentionFeed[] = [
|
||||
{
|
||||
marker: "@",
|
||||
feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
|
||||
feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText, SuggestionMode.SuggestCreateAndLink),
|
||||
itemRenderer: (item) => {
|
||||
const suggestion = item as Suggestion;
|
||||
const itemElement = document.createElement("button");
|
||||
|
||||
@ -29,7 +29,14 @@ export default function MobileDetailMenu() {
|
||||
],
|
||||
selectMenuItemHandler: async ({ command }) => {
|
||||
if (command === "insertChildNote") {
|
||||
note_create.createNote(appContext.tabManager.getActiveContextNotePath() ?? undefined);
|
||||
const parentNoteUrl = appContext.tabManager.getActiveContextNotePath();
|
||||
|
||||
if (parentNoteUrl) {
|
||||
note_create.createNote({
|
||||
target: "into",
|
||||
parentNoteLink: parentNoteUrl,
|
||||
});
|
||||
}
|
||||
} else if (command === "delete") {
|
||||
const notePath = appContext.tabManager.getActiveContextNotePath();
|
||||
if (!notePath) {
|
||||
|
||||
@ -224,7 +224,13 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||
} else if (target.classList.contains("add-note-button")) {
|
||||
const node = $.ui.fancytree.getNode(e as unknown as Event);
|
||||
const parentNotePath = treeService.getNotePath(node);
|
||||
noteCreateService.createNote(parentNotePath, { isProtected: node.data.isProtected });
|
||||
noteCreateService.createNote(
|
||||
{
|
||||
target: "into",
|
||||
parentNoteLink: parentNotePath,
|
||||
isProtected: node.data.isProtected
|
||||
},
|
||||
);
|
||||
} else if (target.classList.contains("enter-workspace-button")) {
|
||||
const node = $.ui.fancytree.getNode(e as unknown as Event);
|
||||
this.triggerCommand("hoistNote", { noteId: node.data.noteId });
|
||||
@ -1403,10 +1409,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||
|
||||
let node: Fancytree.FancytreeNode | null | undefined = await this.expandToNote(activeNotePath, false);
|
||||
|
||||
if (node && node.data.noteId !== treeService.getNoteIdFromUrl(activeNotePath)) {
|
||||
if (node && node.data.noteId !== treeService.getNoteIdFromLink(activeNotePath)) {
|
||||
// if the active note has been moved elsewhere then it won't be found by the path,
|
||||
// so we switch to the alternative of trying to find it by noteId
|
||||
const noteId = treeService.getNoteIdFromUrl(activeNotePath);
|
||||
const noteId = treeService.getNoteIdFromLink(activeNotePath);
|
||||
|
||||
if (noteId) {
|
||||
const notesById = this.getNodesByNoteId(noteId);
|
||||
@ -1836,9 +1842,13 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||
const node = this.getActiveNode();
|
||||
if (!node) return;
|
||||
const notePath = treeService.getNotePath(node);
|
||||
noteCreateService.createNote(notePath, {
|
||||
noteCreateService.createNote(
|
||||
{
|
||||
target: "into",
|
||||
parentNoteLink: notePath,
|
||||
isProtected: node.data.isProtected
|
||||
});
|
||||
}
|
||||
)
|
||||
}
|
||||
}),
|
||||
new TouchBar.TouchBarButton({
|
||||
|
||||
@ -26,7 +26,7 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
|
||||
&& !(await noteContext?.isReadOnly()),
|
||||
toggleCommand: "toggleRibbonTabClassicEditor",
|
||||
content: FormattingToolbar,
|
||||
activate: true,
|
||||
activate: () => !options.is("editedNotesOpenInRibbon"),
|
||||
stayInDom: true
|
||||
},
|
||||
{
|
||||
@ -50,7 +50,7 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
|
||||
icon: "bx bx-calendar-edit",
|
||||
content: EditedNotesTab,
|
||||
show: ({ note }) => note?.hasOwnedLabel("dateNote"),
|
||||
activate: ({ note }) => (note?.getPromotedDefinitionAttributes().length === 0 || !options.is("promotedAttributesOpenInRibbon")) && options.is("editedNotesOpenInRibbon")
|
||||
activate: () => options.is("editedNotesOpenInRibbon")
|
||||
},
|
||||
{
|
||||
title: t("book_properties.book_properties"),
|
||||
|
||||
@ -2,7 +2,7 @@ import { MutableRef, useEffect, useImperativeHandle, useMemo, useRef, useState }
|
||||
import { AttributeEditor as CKEditorAttributeEditor, MentionFeed, ModelElement, ModelNode, ModelPosition } from "@triliumnext/ckeditor5";
|
||||
import { t } from "../../../services/i18n";
|
||||
import server from "../../../services/server";
|
||||
import note_autocomplete, { Suggestion } from "../../../services/note_autocomplete";
|
||||
import note_autocomplete, { SuggestionMode, Suggestion } from "../../../services/note_autocomplete.js";
|
||||
import CKEditor, { CKEditorApi } from "../../react/CKEditor";
|
||||
import { useLegacyImperativeHandlers, useLegacyWidget, useTooltip, useTriliumEvent, useTriliumOption } from "../../react/hooks";
|
||||
import FAttribute from "../../../entities/fattribute";
|
||||
@ -20,6 +20,7 @@ import type { CommandData, FilteredCommandNames } from "../../../components/app_
|
||||
import { AttributeType } from "@triliumnext/commons";
|
||||
import attributes from "../../../services/attributes";
|
||||
import note_create from "../../../services/note_create";
|
||||
import { CreateNoteAction } from "@triliumnext/commons";
|
||||
|
||||
type AttributeCommandNames = FilteredCommandNames<CommandData>;
|
||||
|
||||
@ -33,7 +34,7 @@ const HELP_TEXT = `
|
||||
const mentionSetup: MentionFeed[] = [
|
||||
{
|
||||
marker: "@",
|
||||
feed: (queryText) => note_autocomplete.autocompleteSourceForCKEditor(queryText),
|
||||
feed: (queryText) => note_autocomplete.autocompleteSourceForCKEditor(queryText, SuggestionMode.SuggestCreateAndLink),
|
||||
itemRenderer: (_item) => {
|
||||
const item = _item as Suggestion;
|
||||
const itemElement = document.createElement("button");
|
||||
@ -247,16 +248,18 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
|
||||
|
||||
$el.text(title);
|
||||
},
|
||||
createNoteForReferenceLink: async (title: string) => {
|
||||
let result;
|
||||
if (notePath) {
|
||||
result = await note_create.createNoteWithTypePrompt(notePath, {
|
||||
activate: false,
|
||||
title: title
|
||||
});
|
||||
}
|
||||
|
||||
return result?.note?.getBestNotePathString();
|
||||
createNoteFromCkEditor: async (
|
||||
title: string,
|
||||
parentNotePath: string | undefined,
|
||||
action: CreateNoteAction
|
||||
): Promise<string> => {
|
||||
const { note } = await note_create.createNoteFromAction(
|
||||
action,
|
||||
true,
|
||||
title,
|
||||
parentNotePath,
|
||||
);
|
||||
return note?.getBestNotePathString() ?? "";
|
||||
}
|
||||
}), [ notePath ]));
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import FormGroup from "../react/FormGroup";
|
||||
import NoteAutocomplete from "../react/NoteAutocomplete";
|
||||
import "./Empty.css";
|
||||
import { ParentComponent, refToJQuerySelector } from "../react/react_utils";
|
||||
import note_autocomplete from "../../services/note_autocomplete";
|
||||
import note_autocomplete, { SuggestionMode } from "../../services/note_autocomplete";
|
||||
import appContext from "../../components/app_context";
|
||||
import FNote from "../../entities/fnote";
|
||||
import search from "../../services/search";
|
||||
@ -38,7 +38,7 @@ function NoteSearch({ ntxId }: { ntxId: string | null }) {
|
||||
inputRef={autocompleteRef}
|
||||
opts={{
|
||||
hideGoToSelectedNoteButton: true,
|
||||
allowCreatingNotes: true,
|
||||
suggestionMode: SuggestionMode.SuggestCreateOnly,
|
||||
allowJumpToSearchNotes: true,
|
||||
}}
|
||||
onChange={suggestion => {
|
||||
|
||||
@ -16,7 +16,7 @@ import note_create from "../../../services/note_create";
|
||||
import TouchBar, { TouchBarButton, TouchBarGroup, TouchBarSegmentedControl } from "../../react/TouchBar";
|
||||
import { RefObject } from "preact";
|
||||
import { buildSelectedBackgroundColor } from "../../../components/touch_bar";
|
||||
import { deferred } from "@triliumnext/commons";
|
||||
import { CreateNoteAction, deferred } from "@triliumnext/commons";
|
||||
import { t } from "../../../services/i18n";
|
||||
|
||||
/**
|
||||
@ -115,17 +115,18 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
|
||||
},
|
||||
loadIncludedNote,
|
||||
// Creating notes in @-completion
|
||||
async createNoteForReferenceLink(title: string) {
|
||||
const notePath = noteContext?.notePath;
|
||||
if (!notePath) return;
|
||||
|
||||
const resp = await note_create.createNoteWithTypePrompt(notePath, {
|
||||
activate: false,
|
||||
title: title
|
||||
});
|
||||
|
||||
if (!resp || !resp.note) return;
|
||||
return resp.note.getBestNotePathString();
|
||||
async createNoteFromCkEditor (
|
||||
title: string,
|
||||
parentNotePath: string | undefined,
|
||||
action: CreateNoteAction
|
||||
): Promise<string> {
|
||||
const { note }= await note_create.createNoteFromAction(
|
||||
action,
|
||||
true,
|
||||
title,
|
||||
parentNotePath,
|
||||
)
|
||||
return note?.getBestNotePathString() ?? "";
|
||||
},
|
||||
// Keyboard shortcut
|
||||
async followLinkUnderCursorCommand() {
|
||||
@ -162,7 +163,9 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
|
||||
// without await as this otherwise causes deadlock through component mutex
|
||||
const parentNotePath = appContext.tabManager.getActiveContextNotePath();
|
||||
if (noteContext && parentNotePath) {
|
||||
note_create.createNote(parentNotePath, {
|
||||
note_create.createNote({
|
||||
parentNoteLink: parentNotePath,
|
||||
target: "into",
|
||||
isProtected: note.isProtected,
|
||||
saveSelection: true,
|
||||
textEditor: await noteContext?.getTextEditor()
|
||||
|
||||
@ -7,7 +7,7 @@ import emojiDefinitionsUrl from "@triliumnext/ckeditor5/src/emoji_definitions/en
|
||||
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import { getMermaidConfig } from "../../../services/mermaid.js";
|
||||
import noteAutocompleteService, { type Suggestion } from "../../../services/note_autocomplete.js";
|
||||
import noteAutocompleteService, { SuggestionMode, type Suggestion } from "../../../services/note_autocomplete.js";
|
||||
import mimeTypesService from "../../../services/mime_types.js";
|
||||
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
|
||||
import { buildToolbarConfig } from "./toolbar.js";
|
||||
@ -181,7 +181,7 @@ export async function buildConfig(opts: BuildEditorOptions): Promise<EditorConfi
|
||||
feeds: [
|
||||
{
|
||||
marker: "@",
|
||||
feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
|
||||
feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText, SuggestionMode.SuggestCreateAndLink),
|
||||
itemRenderer: (item) => {
|
||||
const itemElement = document.createElement("button");
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ interface GotoOpts {
|
||||
}
|
||||
|
||||
const BASE_URL = "http://127.0.0.1:8082";
|
||||
const NUM_OF_CREATE_NOTE_OPTIONS = 2;
|
||||
|
||||
interface DropdownLocator extends Locator {
|
||||
selectOptionByText: (text: string) => Promise<void>;
|
||||
@ -73,7 +74,8 @@ export default class App {
|
||||
const resultsSelector = this.currentNoteSplit.locator(".note-detail-empty-results");
|
||||
await expect(resultsSelector).toContainText(noteTitle);
|
||||
await resultsSelector.locator(".aa-suggestion", { hasText: noteTitle })
|
||||
.nth(1) // Select the second one, as the first one is "Create a new note"
|
||||
// Select the n+1 one, as the first one is "Create a new note"
|
||||
.nth(NUM_OF_CREATE_NOTE_OPTIONS)
|
||||
.click();
|
||||
}
|
||||
|
||||
|
||||
@ -80,7 +80,6 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
|
||||
"disableTray",
|
||||
"customSearchEngineName",
|
||||
"customSearchEngineUrl",
|
||||
"promotedAttributesOpenInRibbon",
|
||||
"editedNotesOpenInRibbon",
|
||||
"locale",
|
||||
"formattingLocale",
|
||||
|
||||
@ -129,7 +129,6 @@ const defaultOptions: DefaultOption[] = [
|
||||
{ name: "logRetentionDays", value: "90", isSynced: false }, // default 90 days
|
||||
{ name: "customSearchEngineName", value: "DuckDuckGo", isSynced: true },
|
||||
{ name: "customSearchEngineUrl", value: "https://duckduckgo.com/?q={keyword}", isSynced: true },
|
||||
{ name: "promotedAttributesOpenInRibbon", value: "true", isSynced: true },
|
||||
{ name: "editedNotesOpenInRibbon", value: "true", isSynced: true },
|
||||
{ name: "mfaEnabled", value: "false", isSynced: false },
|
||||
{ name: "mfaMethod", value: "totp", isSynced: false },
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite-plugin-svgo": "~2.0.0",
|
||||
"vitest": "4.0.14",
|
||||
"webdriverio": "9.20.1"
|
||||
"webdriverio": "9.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": "47.2.0"
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite-plugin-svgo": "~2.0.0",
|
||||
"vitest": "4.0.14",
|
||||
"webdriverio": "9.20.1"
|
||||
"webdriverio": "9.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": "47.2.0"
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite-plugin-svgo": "~2.0.0",
|
||||
"vitest": "4.0.14",
|
||||
"webdriverio": "9.20.1"
|
||||
"webdriverio": "9.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": "47.2.0"
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite-plugin-svgo": "~2.0.0",
|
||||
"vitest": "4.0.14",
|
||||
"webdriverio": "9.20.1"
|
||||
"webdriverio": "9.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": "47.2.0"
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite-plugin-svgo": "~2.0.0",
|
||||
"vitest": "4.0.14",
|
||||
"webdriverio": "9.20.1"
|
||||
"webdriverio": "9.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": "47.2.0"
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import "ckeditor5";
|
||||
import { type CreateNoteAction } from "@triliumnext/commons"
|
||||
|
||||
declare global {
|
||||
interface Component {
|
||||
@ -7,7 +8,8 @@ declare global {
|
||||
|
||||
interface EditorComponent extends Component {
|
||||
loadReferenceLinkTitle($el: JQuery<HTMLElement>, href: string): Promise<void>;
|
||||
createNoteForReferenceLink(title: string): Promise<string>;
|
||||
// Must Return Note Path
|
||||
createNoteFromCkEditor(title: string, parentNotePath: string | undefined, action: CreateNoteAction): Promise<string>;
|
||||
loadIncludedNote(noteId: string, $el: JQuery<HTMLElement>): void;
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Command, Mention, Plugin, ModelRange, type ModelSelectable } from "ckeditor5";
|
||||
import { CreateNoteAction } from "@triliumnext/commons"
|
||||
|
||||
/**
|
||||
* Overrides the actions taken by the Mentions plugin (triggered by `@` in the text editor, or `~` & `#` in the attribute editor):
|
||||
@ -36,9 +37,10 @@ interface MentionOpts {
|
||||
|
||||
interface MentionAttribute {
|
||||
id: string;
|
||||
action?: "create-note";
|
||||
action?: CreateNoteAction;
|
||||
noteTitle: string;
|
||||
notePath: string;
|
||||
parentNoteId?: string;
|
||||
}
|
||||
|
||||
class CustomMentionCommand extends Command {
|
||||
@ -56,13 +58,26 @@ class CustomMentionCommand extends Command {
|
||||
model.insertContent( writer.createText( mention.id, {} ), range );
|
||||
});
|
||||
}
|
||||
else if (mention.action === 'create-note') {
|
||||
else if (
|
||||
mention.action === CreateNoteAction.CreateNote ||
|
||||
mention.action === CreateNoteAction.CreateChildNote ||
|
||||
mention.action === CreateNoteAction.CreateAndLinkNote ||
|
||||
mention.action === CreateNoteAction.CreateAndLinkChildNote
|
||||
) {
|
||||
const editorEl = this.editor.editing.view.getDomRoot();
|
||||
const component = glob.getComponentByEl<EditorComponent>(editorEl);
|
||||
|
||||
component.createNoteForReferenceLink(mention.noteTitle).then(notePath => {
|
||||
// use parentNoteId as fallback when notePath is missing
|
||||
const parentNotePath = mention.notePath || mention.parentNoteId;
|
||||
|
||||
component
|
||||
.createNoteFromCkEditor(mention.noteTitle, parentNotePath, mention.action)
|
||||
.then(notePath => {
|
||||
if (notePath) {
|
||||
this.insertReference(range, notePath);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => console.error("Error creating note from CKEditor mention:", err));
|
||||
}
|
||||
else {
|
||||
this.insertReference(range, mention.notePath);
|
||||
|
||||
@ -10,4 +10,5 @@ export * from "./lib/server_api.js";
|
||||
export * from "./lib/shared_constants.js";
|
||||
export * from "./lib/ws_api.js";
|
||||
export * from "./lib/attribute_names.js";
|
||||
export * from "./lib/create_note_actions.js";
|
||||
export * from "./lib/utils.js";
|
||||
|
||||
6
packages/commons/src/lib/create_note_actions.ts
Normal file
6
packages/commons/src/lib/create_note_actions.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export enum CreateNoteAction {
|
||||
CreateNote = "create-note",
|
||||
CreateChildNote = "create-child-note",
|
||||
CreateAndLinkNote = "create-and-link-note",
|
||||
CreateAndLinkChildNote = "create-and-link-child-note"
|
||||
}
|
||||
@ -121,7 +121,6 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
|
||||
downloadImagesAutomatically: boolean;
|
||||
checkForUpdates: boolean;
|
||||
disableTray: boolean;
|
||||
promotedAttributesOpenInRibbon: boolean;
|
||||
editedNotesOpenInRibbon: boolean;
|
||||
codeBlockWordWrap: boolean;
|
||||
textNoteEditorMultilineToolbar: boolean;
|
||||
|
||||
180
pnpm-lock.yaml
generated
180
pnpm-lock.yaml
generated
@ -57,7 +57,7 @@ importers:
|
||||
version: 24.10.1
|
||||
'@vitest/browser-webdriverio':
|
||||
specifier: 4.0.14
|
||||
version: 4.0.14(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(utf-8-validate@6.0.5)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@4.0.14)(webdriverio@9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
version: 4.0.14(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(utf-8-validate@6.0.5)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@4.0.14)(webdriverio@9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 4.0.14
|
||||
version: 4.0.14(@vitest/browser@4.0.14(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(utf-8-validate@6.0.5)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@4.0.14))(vitest@4.0.14)
|
||||
@ -149,11 +149,11 @@ importers:
|
||||
specifier: 19.2.0
|
||||
version: 19.2.0(react@19.2.0)
|
||||
typedoc:
|
||||
specifier: 0.28.14
|
||||
version: 0.28.14(typescript@5.9.3)
|
||||
specifier: 0.28.15
|
||||
version: 0.28.15(typescript@5.9.3)
|
||||
typedoc-plugin-missing-exports:
|
||||
specifier: 4.1.2
|
||||
version: 4.1.2(typedoc@0.28.14(typescript@5.9.3))
|
||||
version: 4.1.2(typedoc@0.28.15(typescript@5.9.3))
|
||||
|
||||
apps/client:
|
||||
dependencies:
|
||||
@ -933,8 +933,8 @@ importers:
|
||||
specifier: 4.0.14
|
||||
version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/browser-webdriverio@4.0.14)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio:
|
||||
specifier: 9.20.1
|
||||
version: 9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
specifier: 9.21.0
|
||||
version: 9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
|
||||
packages/ckeditor5-footnotes:
|
||||
devDependencies:
|
||||
@ -993,8 +993,8 @@ importers:
|
||||
specifier: 4.0.14
|
||||
version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/browser-webdriverio@4.0.14)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio:
|
||||
specifier: 9.20.1
|
||||
version: 9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
specifier: 9.21.0
|
||||
version: 9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
|
||||
packages/ckeditor5-keyboard-marker:
|
||||
devDependencies:
|
||||
@ -1053,8 +1053,8 @@ importers:
|
||||
specifier: 4.0.14
|
||||
version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/browser-webdriverio@4.0.14)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio:
|
||||
specifier: 9.20.1
|
||||
version: 9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
specifier: 9.21.0
|
||||
version: 9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
|
||||
packages/ckeditor5-math:
|
||||
dependencies:
|
||||
@ -1120,8 +1120,8 @@ importers:
|
||||
specifier: 4.0.14
|
||||
version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/browser-webdriverio@4.0.14)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio:
|
||||
specifier: 9.20.1
|
||||
version: 9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
specifier: 9.21.0
|
||||
version: 9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
|
||||
packages/ckeditor5-mermaid:
|
||||
dependencies:
|
||||
@ -1187,8 +1187,8 @@ importers:
|
||||
specifier: 4.0.14
|
||||
version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/browser-webdriverio@4.0.14)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio:
|
||||
specifier: 9.20.1
|
||||
version: 9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
specifier: 9.21.0
|
||||
version: 9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
|
||||
packages/codemirror:
|
||||
dependencies:
|
||||
@ -3164,8 +3164,8 @@ packages:
|
||||
'@gar/promisify@1.1.3':
|
||||
resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
|
||||
|
||||
'@gerrit0/mini-shiki@3.14.0':
|
||||
resolution: {integrity: sha512-c5X8fwPLOtUS8TVdqhynz9iV0GlOtFUT1ppXYzUUlEXe4kbZ/mvMT8wXoT8kCwUka+zsiloq7sD3pZ3+QVTuNQ==}
|
||||
'@gerrit0/mini-shiki@3.17.0':
|
||||
resolution: {integrity: sha512-Bpf6WuFar20ZXL6qU6VpVl4bVQfyyYiX+6O4xrns4nkU3Mr8paeupDbS1HENpcLOYj7pN4Rkd/yCaPA0vQwKww==}
|
||||
|
||||
'@hapi/hoek@9.3.0':
|
||||
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
|
||||
@ -4701,17 +4701,17 @@ packages:
|
||||
'@selderee/plugin-htmlparser2@0.11.0':
|
||||
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
|
||||
|
||||
'@shikijs/engine-oniguruma@3.14.0':
|
||||
resolution: {integrity: sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug==}
|
||||
'@shikijs/engine-oniguruma@3.17.0':
|
||||
resolution: {integrity: sha512-flSbHZAiOZDNTrEbULY8DLWavu/TyVu/E7RChpLB4WvKX4iHMfj80C6Hi3TjIWaQtHOW0KC6kzMcuB5TO1hZ8Q==}
|
||||
|
||||
'@shikijs/langs@3.14.0':
|
||||
resolution: {integrity: sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg==}
|
||||
'@shikijs/langs@3.17.0':
|
||||
resolution: {integrity: sha512-icmur2n5Ojb+HAiQu6NEcIIJ8oWDFGGEpiqSCe43539Sabpx7Y829WR3QuUW2zjTM4l6V8Sazgb3rrHO2orEAw==}
|
||||
|
||||
'@shikijs/themes@3.14.0':
|
||||
resolution: {integrity: sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA==}
|
||||
'@shikijs/themes@3.17.0':
|
||||
resolution: {integrity: sha512-/xEizMHLBmMHwtx4JuOkRf3zwhWD2bmG5BRr0IPjpcWpaq4C3mYEuTk/USAEglN0qPrTwEHwKVpSu/y2jhferA==}
|
||||
|
||||
'@shikijs/types@3.14.0':
|
||||
resolution: {integrity: sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ==}
|
||||
'@shikijs/types@3.17.0':
|
||||
resolution: {integrity: sha512-wjLVfutYWVUnxAjsWEob98xgyaGv0dTEnMZDruU5mRjVN7szcGOfgO+997W2yR6odp+1PtSBNeSITRRTfUzK/g==}
|
||||
|
||||
'@shikijs/vscode-textmate@10.0.2':
|
||||
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
|
||||
@ -5449,9 +5449,6 @@ packages:
|
||||
'@types/node@16.9.1':
|
||||
resolution: {integrity: sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==}
|
||||
|
||||
'@types/node@20.19.24':
|
||||
resolution: {integrity: sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==}
|
||||
|
||||
'@types/node@20.19.25':
|
||||
resolution: {integrity: sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==}
|
||||
|
||||
@ -5852,8 +5849,8 @@ packages:
|
||||
'@vue/shared@3.5.14':
|
||||
resolution: {integrity: sha512-oXTwNxVfc9EtP1zzXAlSlgARLXNC84frFYkS0HHz0h3E4WZSP9sywqjqzGCP9Y34M8ipNmd380pVgmMuwELDyQ==}
|
||||
|
||||
'@wdio/config@9.20.1':
|
||||
resolution: {integrity: sha512-npl2J+rjCDJPjVySgWpciOyhWddn6s7n5sepKXLR7x1ADQHl5zUFv1dHD3jx4OQ9l6lrGQSPaofuz+7e9mu+vg==}
|
||||
'@wdio/config@9.21.0':
|
||||
resolution: {integrity: sha512-8TP5/q+Agjc43LET1f0LhLmuEI803O3QtZEbSxOkkvJ7/e1jDWPm4qsL7SjQJlx8xGrW0kwRlPl7+U9Sr0dhCQ==}
|
||||
engines: {node: '>=18.20.0'}
|
||||
|
||||
'@wdio/logger@9.18.0':
|
||||
@ -5871,8 +5868,8 @@ packages:
|
||||
resolution: {integrity: sha512-zMmAtse2UMCSOW76mvK3OejauAdcFGuKopNRH7crI0gwKTZtvV89yXWRziz9cVXpFgfmJCjf9edxKFWdhuF5yw==}
|
||||
engines: {node: '>=18.20.0'}
|
||||
|
||||
'@wdio/utils@9.20.1':
|
||||
resolution: {integrity: sha512-C/Gsy5NAatsGUF1eT9Ks/ErR52/X4YI7MSm7BtwNOw8v2Ko+SiCA5qXts61J0A7QYwOn4gfXfBZZnzSAng6G/w==}
|
||||
'@wdio/utils@9.21.0':
|
||||
resolution: {integrity: sha512-aj8ao2V/e6Sv9gZby2ZIj4dMLjwYVba47Nlr+pOfK8N4VKKU0VRLPzvTlfK1HWaoS6u/GBbVx2pefYRrvd72BQ==}
|
||||
engines: {node: '>=18.20.0'}
|
||||
|
||||
'@webassemblyjs/ast@1.14.1':
|
||||
@ -5933,6 +5930,10 @@ packages:
|
||||
'@xtuc/long@4.2.2':
|
||||
resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
|
||||
|
||||
'@zip.js/zip.js@2.8.11':
|
||||
resolution: {integrity: sha512-0fztsk/0ryJ+2PPr9EyXS5/Co7OK8q3zY/xOoozEWaUsL5x+C0cyZ4YyMuUffOO2Dx/rAdq4JMPqW0VUtm+vzA==}
|
||||
engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=18.0.0'}
|
||||
|
||||
'@zip.js/zip.js@2.8.2':
|
||||
resolution: {integrity: sha512-PI6UdgpSeVoGvzguKHmy2bwOqI3UYkntLZOCpyJSKIi7234c5aJmQYkJB/P4P2YUJkqhbqvu7iM2/0eJZ178nA==}
|
||||
engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=16.5.0'}
|
||||
@ -8599,9 +8600,9 @@ packages:
|
||||
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
||||
deprecated: This package is no longer supported.
|
||||
|
||||
geckodriver@5.0.0:
|
||||
resolution: {integrity: sha512-vn7TtQ3b9VMJtVXsyWtQQl1fyBVFhQy7UvJF96kPuuJ0or5THH496AD3eUyaDD11+EqCxH9t6V+EP9soZQk4YQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
geckodriver@6.1.0:
|
||||
resolution: {integrity: sha512-ZRXLa4ZaYTTgUO4Eefw+RsQCleugU2QLb1ME7qTYxxuRj51yAhfnXaItXNs5/vUzfIaDHuZ+YnSF005hfp07nQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
hasBin: true
|
||||
|
||||
generate-function@2.3.1:
|
||||
@ -10616,6 +10617,10 @@ packages:
|
||||
resolution: {integrity: sha512-2emPTb1reeLLYwHxyVx993iYyCHEiRRO+y8NFXFPL5kl5q14sgTK76cXyEKkeKCHeRw35SfdkUJ10Q1KfHuiIQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
modern-tar@0.7.2:
|
||||
resolution: {integrity: sha512-TGG1ZRk1TAQ3neuZwahAHke3rKsSlro+ooMYtjh9sl2gGPVMLMuWiHgwC7im9T5bSM566RSo2Dko56ETgEvZcA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
morphdom@2.7.7:
|
||||
resolution: {integrity: sha512-04GmsiBcalrSCNmzfo+UjU8tt3PhZJKzcOy+r1FlGA7/zri8wre3I1WkYN9PT3sIeIKfW9bpyElA+VzOg2E24g==}
|
||||
|
||||
@ -14043,8 +14048,8 @@ packages:
|
||||
peerDependencies:
|
||||
typedoc: ^0.28.1
|
||||
|
||||
typedoc@0.28.14:
|
||||
resolution: {integrity: sha512-ftJYPvpVfQvFzpkoSfHLkJybdA/geDJ8BGQt/ZnkkhnBYoYW6lBgPQXu6vqLxO4X75dA55hX8Af847H5KXlEFA==}
|
||||
typedoc@0.28.15:
|
||||
resolution: {integrity: sha512-mw2/2vTL7MlT+BVo43lOsufkkd2CJO4zeOSuWQQsiXoV2VuEn7f6IZp2jsUDPmBMABpgR0R5jlcJ2OGEFYmkyg==}
|
||||
engines: {node: '>= 18', pnpm: '>= 10'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@ -14525,12 +14530,12 @@ packages:
|
||||
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
webdriver@9.20.1:
|
||||
resolution: {integrity: sha512-QtvYqPai2NOnq7qePPH6kNSwk7+tnmSvnlOnY8dIT/Y24TPdQp44NjF/BUYAWIlqoBlZqHClQFTSVwT2qvO2Tw==}
|
||||
webdriver@9.21.0:
|
||||
resolution: {integrity: sha512-XLOhpU/EFPo4TMk+0fRli4g1WriUujxrfDxGT/QRq0MJsfhSYPF8FdefFdL5gHIrJfSKscaQHGWkbnsHftfqeg==}
|
||||
engines: {node: '>=18.20.0'}
|
||||
|
||||
webdriverio@9.20.1:
|
||||
resolution: {integrity: sha512-QVM/asb5sDESz37ow/BAOA0z2HtUJsuAjPKHdw+Vx92PaQP3EfHwTgxK2T5rgwa0WRNh+c+n/0nEqIvqBl01sA==}
|
||||
webdriverio@9.21.0:
|
||||
resolution: {integrity: sha512-7teaXajOuNdn2UyyKlqMLssJjf0vDEih+Lo+tE/gHOt/P+mB8CinZym4PGtsriZLcyt4xV+Cun3hDmXM+pL26A==}
|
||||
engines: {node: '>=18.20.0'}
|
||||
peerDependencies:
|
||||
puppeteer-core: '>=22.x || <=24.x'
|
||||
@ -16042,8 +16047,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-multi-root@47.2.0':
|
||||
dependencies:
|
||||
@ -16093,6 +16096,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-core': 47.2.0
|
||||
'@ckeditor/ckeditor5-engine': 47.2.0
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-essentials@47.2.0':
|
||||
dependencies:
|
||||
@ -16749,8 +16754,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-icons': 47.2.0
|
||||
'@ckeditor/ckeditor5-ui': 47.2.0
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-upload@47.2.0':
|
||||
dependencies:
|
||||
@ -18056,12 +18059,12 @@ snapshots:
|
||||
|
||||
'@gar/promisify@1.1.3': {}
|
||||
|
||||
'@gerrit0/mini-shiki@3.14.0':
|
||||
'@gerrit0/mini-shiki@3.17.0':
|
||||
dependencies:
|
||||
'@shikijs/engine-oniguruma': 3.14.0
|
||||
'@shikijs/langs': 3.14.0
|
||||
'@shikijs/themes': 3.14.0
|
||||
'@shikijs/types': 3.14.0
|
||||
'@shikijs/engine-oniguruma': 3.17.0
|
||||
'@shikijs/langs': 3.17.0
|
||||
'@shikijs/themes': 3.17.0
|
||||
'@shikijs/types': 3.17.0
|
||||
'@shikijs/vscode-textmate': 10.0.2
|
||||
|
||||
'@hapi/hoek@9.3.0': {}
|
||||
@ -19777,20 +19780,20 @@ snapshots:
|
||||
domhandler: 5.0.3
|
||||
selderee: 0.11.0
|
||||
|
||||
'@shikijs/engine-oniguruma@3.14.0':
|
||||
'@shikijs/engine-oniguruma@3.17.0':
|
||||
dependencies:
|
||||
'@shikijs/types': 3.14.0
|
||||
'@shikijs/types': 3.17.0
|
||||
'@shikijs/vscode-textmate': 10.0.2
|
||||
|
||||
'@shikijs/langs@3.14.0':
|
||||
'@shikijs/langs@3.17.0':
|
||||
dependencies:
|
||||
'@shikijs/types': 3.14.0
|
||||
'@shikijs/types': 3.17.0
|
||||
|
||||
'@shikijs/themes@3.14.0':
|
||||
'@shikijs/themes@3.17.0':
|
||||
dependencies:
|
||||
'@shikijs/types': 3.14.0
|
||||
'@shikijs/types': 3.17.0
|
||||
|
||||
'@shikijs/types@3.14.0':
|
||||
'@shikijs/types@3.17.0':
|
||||
dependencies:
|
||||
'@shikijs/vscode-textmate': 10.0.2
|
||||
'@types/hast': 3.0.4
|
||||
@ -20725,7 +20728,7 @@ snapshots:
|
||||
|
||||
'@types/mute-stream@0.0.4':
|
||||
dependencies:
|
||||
'@types/node': 22.19.1
|
||||
'@types/node': 24.10.1
|
||||
|
||||
'@types/node-forge@1.3.14':
|
||||
dependencies:
|
||||
@ -20733,10 +20736,6 @@ snapshots:
|
||||
|
||||
'@types/node@16.9.1': {}
|
||||
|
||||
'@types/node@20.19.24':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/node@20.19.25':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
@ -20914,7 +20913,7 @@ snapshots:
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
dependencies:
|
||||
'@types/node': 22.18.13
|
||||
'@types/node': 24.10.1
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
@ -21141,11 +21140,11 @@ snapshots:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@vitest/browser-webdriverio@4.0.14(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(utf-8-validate@6.0.5)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@4.0.14)(webdriverio@9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5))':
|
||||
'@vitest/browser-webdriverio@4.0.14(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(utf-8-validate@6.0.5)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@4.0.14)(webdriverio@9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))':
|
||||
dependencies:
|
||||
'@vitest/browser': 4.0.14(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(utf-8-validate@6.0.5)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@4.0.14)
|
||||
vitest: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/browser-webdriverio@4.0.14)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio: 9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
webdriverio: 9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- msw
|
||||
@ -21301,11 +21300,11 @@ snapshots:
|
||||
|
||||
'@vue/shared@3.5.14': {}
|
||||
|
||||
'@wdio/config@9.20.1':
|
||||
'@wdio/config@9.21.0':
|
||||
dependencies:
|
||||
'@wdio/logger': 9.18.0
|
||||
'@wdio/types': 9.20.0
|
||||
'@wdio/utils': 9.20.1
|
||||
'@wdio/utils': 9.21.0
|
||||
deepmerge-ts: 7.1.5
|
||||
glob: 10.4.5
|
||||
import-meta-resolve: 4.2.0
|
||||
@ -21325,13 +21324,13 @@ snapshots:
|
||||
|
||||
'@wdio/repl@9.16.2':
|
||||
dependencies:
|
||||
'@types/node': 20.19.24
|
||||
'@types/node': 20.19.25
|
||||
|
||||
'@wdio/types@9.20.0':
|
||||
dependencies:
|
||||
'@types/node': 20.19.24
|
||||
'@types/node': 20.19.25
|
||||
|
||||
'@wdio/utils@9.20.1':
|
||||
'@wdio/utils@9.21.0':
|
||||
dependencies:
|
||||
'@puppeteer/browsers': 2.10.10
|
||||
'@wdio/logger': 9.18.0
|
||||
@ -21339,7 +21338,7 @@ snapshots:
|
||||
decamelize: 6.0.1
|
||||
deepmerge-ts: 7.1.5
|
||||
edgedriver: 6.1.2
|
||||
geckodriver: 5.0.0
|
||||
geckodriver: 6.1.0
|
||||
get-port: 7.1.0
|
||||
import-meta-resolve: 4.2.0
|
||||
locate-app: 2.5.0
|
||||
@ -21435,6 +21434,8 @@ snapshots:
|
||||
|
||||
'@xtuc/long@4.2.2': {}
|
||||
|
||||
'@zip.js/zip.js@2.8.11': {}
|
||||
|
||||
'@zip.js/zip.js@2.8.2': {}
|
||||
|
||||
abab@2.0.6: {}
|
||||
@ -24932,18 +24933,15 @@ snapshots:
|
||||
wide-align: 1.1.5
|
||||
optional: true
|
||||
|
||||
geckodriver@5.0.0:
|
||||
geckodriver@6.1.0:
|
||||
dependencies:
|
||||
'@wdio/logger': 9.18.0
|
||||
'@zip.js/zip.js': 2.8.2
|
||||
'@zip.js/zip.js': 2.8.11
|
||||
decamelize: 6.0.1
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
node-fetch: 3.3.2
|
||||
tar-fs: 3.1.1
|
||||
which: 5.0.0
|
||||
modern-tar: 0.7.2
|
||||
transitivePeerDependencies:
|
||||
- bare-buffer
|
||||
- supports-color
|
||||
|
||||
generate-function@2.3.1:
|
||||
@ -27452,6 +27450,8 @@ snapshots:
|
||||
hasown: 2.0.2
|
||||
isarray: 2.0.5
|
||||
|
||||
modern-tar@0.7.2: {}
|
||||
|
||||
morphdom@2.7.7: {}
|
||||
|
||||
mrmime@2.0.1: {}
|
||||
@ -31446,13 +31446,13 @@ snapshots:
|
||||
|
||||
typedarray@0.0.6: {}
|
||||
|
||||
typedoc-plugin-missing-exports@4.1.2(typedoc@0.28.14(typescript@5.9.3)):
|
||||
typedoc-plugin-missing-exports@4.1.2(typedoc@0.28.15(typescript@5.9.3)):
|
||||
dependencies:
|
||||
typedoc: 0.28.14(typescript@5.9.3)
|
||||
typedoc: 0.28.15(typescript@5.9.3)
|
||||
|
||||
typedoc@0.28.14(typescript@5.9.3):
|
||||
typedoc@0.28.15(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@gerrit0/mini-shiki': 3.14.0
|
||||
'@gerrit0/mini-shiki': 3.17.0
|
||||
lunr: 2.3.9
|
||||
markdown-it: 14.1.0
|
||||
minimatch: 9.0.5
|
||||
@ -31824,7 +31824,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@types/node': 24.10.1
|
||||
'@vitest/browser-webdriverio': 4.0.14(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(utf-8-validate@6.0.5)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@4.0.14)(webdriverio@9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@vitest/browser-webdriverio': 4.0.14(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.1)(typescript@5.9.3))(utf-8-validate@6.0.5)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@4.0.14)(webdriverio@9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@vitest/ui': 4.0.14(vitest@4.0.14)
|
||||
happy-dom: 20.0.11
|
||||
jsdom: 26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
@ -31914,15 +31914,15 @@ snapshots:
|
||||
|
||||
web-streams-polyfill@3.3.3: {}
|
||||
|
||||
webdriver@9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5):
|
||||
webdriver@9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5):
|
||||
dependencies:
|
||||
'@types/node': 20.19.24
|
||||
'@types/node': 20.19.25
|
||||
'@types/ws': 8.18.1
|
||||
'@wdio/config': 9.20.1
|
||||
'@wdio/config': 9.21.0
|
||||
'@wdio/logger': 9.18.0
|
||||
'@wdio/protocols': 9.16.2
|
||||
'@wdio/types': 9.20.0
|
||||
'@wdio/utils': 9.20.1
|
||||
'@wdio/utils': 9.21.0
|
||||
deepmerge-ts: 7.1.5
|
||||
https-proxy-agent: 7.0.6
|
||||
undici: 6.21.3
|
||||
@ -31933,16 +31933,16 @@ snapshots:
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
webdriverio@9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5):
|
||||
webdriverio@9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5):
|
||||
dependencies:
|
||||
'@types/node': 20.19.24
|
||||
'@types/node': 20.19.25
|
||||
'@types/sinonjs__fake-timers': 8.1.5
|
||||
'@wdio/config': 9.20.1
|
||||
'@wdio/config': 9.21.0
|
||||
'@wdio/logger': 9.18.0
|
||||
'@wdio/protocols': 9.16.2
|
||||
'@wdio/repl': 9.16.2
|
||||
'@wdio/types': 9.20.0
|
||||
'@wdio/utils': 9.20.1
|
||||
'@wdio/utils': 9.21.0
|
||||
archiver: 7.0.1
|
||||
aria-query: 5.3.2
|
||||
cheerio: 1.1.2
|
||||
@ -31959,7 +31959,7 @@ snapshots:
|
||||
rgb2hex: 0.2.5
|
||||
serialize-error: 12.0.0
|
||||
urlpattern-polyfill: 10.1.0
|
||||
webdriver: 9.20.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
webdriver: 9.21.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
transitivePeerDependencies:
|
||||
- bare-buffer
|
||||
- bufferutil
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user