mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-28 02:22:26 +08:00
Merge pull request #2029 from TriliumNext/refactor/typecheck_errors
Solve typecheck errors
This commit is contained in:
commit
63ea9104c6
2
.github/workflows/dev.yml
vendored
2
.github/workflows/dev.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: nrwl/nx-set-shas@v4
|
- uses: nrwl/nx-set-shas@v4
|
||||||
- name: Check affected
|
- name: Check affected
|
||||||
run: pnpm nx affected -t build rebuild-deps
|
run: pnpm nx affected -t typecheck build rebuild-deps
|
||||||
|
|
||||||
report-electron-size:
|
report-electron-size:
|
||||||
name: Report Electron size
|
name: Report Electron size
|
||||||
|
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -26,5 +26,7 @@
|
|||||||
".github/workflows/nightly.yml"
|
".github/workflows/nightly.yml"
|
||||||
],
|
],
|
||||||
"typescript.validate.enable": true,
|
"typescript.validate.enable": true,
|
||||||
"typescript.tsserver.experimental.enableProjectDiagnostics": true
|
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||||
}
|
}
|
@ -27,6 +27,7 @@ import type EditableTextTypeWidget from "../widgets/type_widgets/editable_text.j
|
|||||||
import type { NativeImage, TouchBar } from "electron";
|
import type { NativeImage, TouchBar } from "electron";
|
||||||
import TouchBarComponent from "./touch_bar.js";
|
import TouchBarComponent from "./touch_bar.js";
|
||||||
import type { CKTextEditor } from "@triliumnext/ckeditor5";
|
import type { CKTextEditor } from "@triliumnext/ckeditor5";
|
||||||
|
import type CodeMirror from "@triliumnext/codemirror";
|
||||||
|
|
||||||
interface Layout {
|
interface Layout {
|
||||||
getRootWidget: (appContext: AppContext) => RootWidget;
|
getRootWidget: (appContext: AppContext) => RootWidget;
|
||||||
@ -191,7 +192,7 @@ export type CommandMappings = {
|
|||||||
ExecuteCommandData<CKTextEditor> & {
|
ExecuteCommandData<CKTextEditor> & {
|
||||||
callback?: GetTextEditorCallback;
|
callback?: GetTextEditorCallback;
|
||||||
};
|
};
|
||||||
executeWithCodeEditor: CommandData & ExecuteCommandData<CodeMirrorInstance>;
|
executeWithCodeEditor: CommandData & ExecuteCommandData<CodeMirror>;
|
||||||
/**
|
/**
|
||||||
* Called upon when attempting to retrieve the content element of a {@link NoteContext}.
|
* Called upon when attempting to retrieve the content element of a {@link NoteContext}.
|
||||||
* Generally should not be invoked manually, as it is used by {@link NoteContext.getContentElement}.
|
* Generally should not be invoked manually, as it is used by {@link NoteContext.getContentElement}.
|
||||||
|
@ -54,7 +54,7 @@ export default class TouchBarComponent extends Component {
|
|||||||
#refreshTouchBar() {
|
#refreshTouchBar() {
|
||||||
const { TouchBar } = this.remote;
|
const { TouchBar } = this.remote;
|
||||||
const parentComponent = this.lastFocusedComponent;
|
const parentComponent = this.lastFocusedComponent;
|
||||||
let touchBar = null;
|
let touchBar: Electron.CrossProcessExports.TouchBar | null = null;
|
||||||
|
|
||||||
if (this.$activeModal?.length) {
|
if (this.$activeModal?.length) {
|
||||||
touchBar = this.#buildModalTouchBar();
|
touchBar = this.#buildModalTouchBar();
|
||||||
|
@ -789,7 +789,7 @@ class FNote {
|
|||||||
*/
|
*/
|
||||||
async getRelationTargets(name: string) {
|
async getRelationTargets(name: string) {
|
||||||
const relations = this.getRelations(name);
|
const relations = this.getRelations(name);
|
||||||
const targets = [];
|
const targets: (FNote | null)[] = [];
|
||||||
|
|
||||||
for (const relation of relations) {
|
for (const relation of relations) {
|
||||||
targets.push(await this.froca.getNote(relation.value));
|
targets.push(await this.froca.getNote(relation.value));
|
||||||
|
@ -80,7 +80,7 @@ async function copy(branchIds: string[]) {
|
|||||||
if (utils.isElectron()) {
|
if (utils.isElectron()) {
|
||||||
// https://github.com/zadam/trilium/issues/2401
|
// https://github.com/zadam/trilium/issues/2401
|
||||||
const { clipboard } = require("electron");
|
const { clipboard } = require("electron");
|
||||||
const links = [];
|
const links: string[] = [];
|
||||||
|
|
||||||
for (const branch of froca.getBranches(clipboardBranchIds)) {
|
for (const branch of froca.getBranches(clipboardBranchIds)) {
|
||||||
const $link = await linkService.createLink(`${branch.parentNoteId}/${branch.noteId}`, { referenceLink: true });
|
const $link = await linkService.createLink(`${branch.parentNoteId}/${branch.noteId}`, { referenceLink: true });
|
||||||
|
@ -50,7 +50,7 @@ async function processEntityChanges(entityChanges: EntityChange[]) {
|
|||||||
// To this we count: standard parent-child relationships and template/inherit relations (attribute inheritance follows them).
|
// To this we count: standard parent-child relationships and template/inherit relations (attribute inheritance follows them).
|
||||||
// Here we watch for changes which might violate this principle - e.g., an introduction of a new "inherit" relation might
|
// Here we watch for changes which might violate this principle - e.g., an introduction of a new "inherit" relation might
|
||||||
// mean we need to load the target of the relation (and then perhaps transitively the whole note path of this target).
|
// mean we need to load the target of the relation (and then perhaps transitively the whole note path of this target).
|
||||||
const missingNoteIds = [];
|
const missingNoteIds: string[] = [];
|
||||||
|
|
||||||
for (const { entityName, entity } of entityChanges) {
|
for (const { entityName, entity } of entityChanges) {
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
|
@ -215,9 +215,9 @@ export function parseNavigationStateFromUrl(url: string | undefined) {
|
|||||||
const viewScope: ViewScope = {
|
const viewScope: ViewScope = {
|
||||||
viewMode: "default"
|
viewMode: "default"
|
||||||
};
|
};
|
||||||
let ntxId = null;
|
let ntxId: string | null = null;
|
||||||
let hoistedNoteId = null;
|
let hoistedNoteId: string | null = null;
|
||||||
let searchString = null;
|
let searchString: string | null = null;
|
||||||
|
|
||||||
if (paramString) {
|
if (paramString) {
|
||||||
for (const pair of paramString.split("&")) {
|
for (const pair of paramString.split("&")) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
type LabelType = "text" | "number" | "boolean" | "date" | "datetime" | "time" | "url";
|
type LabelType = "text" | "number" | "boolean" | "date" | "datetime" | "time" | "url";
|
||||||
type Multiplicity = "single" | "multi";
|
type Multiplicity = "single" | "multi";
|
||||||
|
|
||||||
interface DefinitionObject {
|
export interface DefinitionObject {
|
||||||
isPromoted?: boolean;
|
isPromoted?: boolean;
|
||||||
labelType?: LabelType;
|
labelType?: LabelType;
|
||||||
multiplicity?: Multiplicity;
|
multiplicity?: Multiplicity;
|
||||||
|
@ -97,7 +97,7 @@ export function loadHighlightingTheme(themeName: string) {
|
|||||||
*/
|
*/
|
||||||
export function isSyntaxHighlightEnabled() {
|
export function isSyntaxHighlightEnabled() {
|
||||||
const theme = options.get("codeBlockTheme");
|
const theme = options.get("codeBlockTheme");
|
||||||
return theme && theme !== "none";
|
return !!theme && theme !== "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,8 +34,8 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root
|
|||||||
path.push("root");
|
path.push("root");
|
||||||
}
|
}
|
||||||
|
|
||||||
const effectivePathSegments = [];
|
const effectivePathSegments: string[] = [];
|
||||||
let childNoteId = null;
|
let childNoteId: string | null = null;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -197,7 +197,7 @@ function getNotePath(node: Fancytree.FancytreeNode) {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = [];
|
const path: string[] = [];
|
||||||
|
|
||||||
while (node) {
|
while (node) {
|
||||||
if (node.data.noteId) {
|
if (node.data.noteId) {
|
||||||
@ -236,7 +236,7 @@ async function getNoteTitle(noteId: string, parentNoteId: string | null = null)
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getNotePathTitleComponents(notePath: string) {
|
async function getNotePathTitleComponents(notePath: string) {
|
||||||
const titleComponents = [];
|
const titleComponents: string[] = [];
|
||||||
|
|
||||||
if (notePath.startsWith("root/")) {
|
if (notePath.startsWith("root/")) {
|
||||||
notePath = notePath.substr(5);
|
notePath = notePath.substr(5);
|
||||||
|
@ -51,7 +51,7 @@ function getMonthsInDateRange(startDate: string, endDate: string) {
|
|||||||
const end = endDate.split("-");
|
const end = endDate.split("-");
|
||||||
const startYear = parseInt(start[0]);
|
const startYear = parseInt(start[0]);
|
||||||
const endYear = parseInt(end[0]);
|
const endYear = parseInt(end[0]);
|
||||||
const dates = [];
|
const dates: string[] = [];
|
||||||
|
|
||||||
for (let i = startYear; i <= endYear; i++) {
|
for (let i = startYear; i <= endYear; i++) {
|
||||||
const endMonth = i != endYear ? 11 : parseInt(end[1]) - 1;
|
const endMonth = i != endYear ? 11 : parseInt(end[1]) - 1;
|
||||||
@ -84,7 +84,7 @@ function formatTimeInterval(ms: number) {
|
|||||||
const hours = Math.floor(minutes / 60);
|
const hours = Math.floor(minutes / 60);
|
||||||
const days = Math.floor(hours / 24);
|
const days = Math.floor(hours / 24);
|
||||||
const plural = (count: number, name: string) => `${count} ${name}${count > 1 ? "s" : ""}`;
|
const plural = (count: number, name: string) => `${count} ${name}${count > 1 ? "s" : ""}`;
|
||||||
const segments = [];
|
const segments: string[] = [];
|
||||||
|
|
||||||
if (days > 0) {
|
if (days > 0) {
|
||||||
segments.push(plural(days, "day"));
|
segments.push(plural(days, "day"));
|
||||||
@ -149,7 +149,7 @@ function isMac() {
|
|||||||
|
|
||||||
export const hasTouchBar = (isMac() && isElectron());
|
export const hasTouchBar = (isMac() && isElectron());
|
||||||
|
|
||||||
function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.ContextMenuEvent | JQuery.TriggeredEvent | React.PointerEvent<HTMLCanvasElement>) {
|
function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.ContextMenuEvent | JQuery.TriggeredEvent | React.PointerEvent<HTMLCanvasElement> | JQueryEventObject) {
|
||||||
return (!isMac() && evt.ctrlKey) || (isMac() && evt.metaKey);
|
return (!isMac() && evt.ctrlKey) || (isMac() && evt.metaKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ interface NoteDefinition extends AttributeDefinitions, RelationDefinitions {
|
|||||||
* ]);
|
* ]);
|
||||||
*/
|
*/
|
||||||
export function buildNotes(notes: NoteDefinition[]) {
|
export function buildNotes(notes: NoteDefinition[]) {
|
||||||
const ids = [];
|
const ids: string[] = [];
|
||||||
|
|
||||||
for (const noteDef of notes) {
|
for (const noteDef of notes) {
|
||||||
ids.push(buildNote(noteDef).noteId);
|
ids.push(buildNote(noteDef).noteId);
|
||||||
|
@ -718,7 +718,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildDefinitionValue() {
|
buildDefinitionValue() {
|
||||||
const props = [];
|
const props: string[] = [];
|
||||||
|
|
||||||
if (this.$inputPromoted.is(":checked")) {
|
if (this.$inputPromoted.is(":checked")) {
|
||||||
props.push("promoted");
|
props.push("promoted");
|
||||||
@ -728,10 +728,10 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
props.push(this.$inputMultiplicity.val());
|
props.push(this.$inputMultiplicity.val() as string);
|
||||||
|
|
||||||
if (this.attrType === "label-definition") {
|
if (this.attrType === "label-definition") {
|
||||||
props.push(this.$inputLabelType.val());
|
props.push(this.$inputLabelType.val() as string);
|
||||||
|
|
||||||
if (this.$inputLabelType.val() === "number" && this.$inputNumberPrecision.val() !== "") {
|
if (this.$inputLabelType.val() === "number" && this.$inputNumberPrecision.val() !== "") {
|
||||||
props.push(`precision=${this.$inputNumberPrecision.val()}`);
|
props.push(`precision=${this.$inputNumberPrecision.val()}`);
|
||||||
|
@ -29,7 +29,7 @@ export default class LauncherContainer extends FlexContainer<LauncherWidget> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newChildren = [];
|
const newChildren: LauncherWidget[] = [];
|
||||||
|
|
||||||
for (const launcherNote of await visibleLaunchersRoot.getChildNotes()) {
|
for (const launcherNote of await visibleLaunchersRoot.getChildNotes()) {
|
||||||
try {
|
try {
|
||||||
|
@ -217,7 +217,7 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshNotShown(data: NoteSwitchedContext | EventData<"activeContextChanged">) {
|
refreshNotShown(data: NoteSwitchedContext | EventData<"activeContextChanged">) {
|
||||||
const promises = [];
|
const promises: (Promise<unknown> | null | undefined)[] = [];
|
||||||
|
|
||||||
for (const subContext of data.noteContext.getMainContext().getSubContexts()) {
|
for (const subContext of data.noteContext.getMainContext().getSubContexts()) {
|
||||||
if (!subContext.ntxId) {
|
if (!subContext.ntxId) {
|
||||||
|
@ -2,16 +2,6 @@ import type { FindAndReplaceState, FindCommandResult } from "@triliumnext/ckedit
|
|||||||
import type { FindResult } from "./find.js";
|
import type { FindResult } from "./find.js";
|
||||||
import type FindWidget from "./find.js";
|
import type FindWidget from "./find.js";
|
||||||
|
|
||||||
// TODO: Deduplicate.
|
|
||||||
interface Match {
|
|
||||||
className: string;
|
|
||||||
clear(): void;
|
|
||||||
find(): {
|
|
||||||
from: number;
|
|
||||||
to: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class FindInText {
|
export default class FindInText {
|
||||||
|
|
||||||
private parent: FindWidget;
|
private parent: FindWidget;
|
||||||
@ -35,7 +25,7 @@ export default class FindInText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const model = textEditor.model;
|
const model = textEditor.model;
|
||||||
let findResult = null;
|
let findResult: FindCommandResult | null = null;
|
||||||
let totalFound = 0;
|
let totalFound = 0;
|
||||||
let currentFound = -1;
|
let currentFound = -1;
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
|
|||||||
// Used to determine if a string is only a formula
|
// Used to determine if a string is only a formula
|
||||||
const onlyMathRegex = /^<span class="math-tex">\\\([^\)]*?\)<\/span>(?:<span class="math-tex">\\\([^\)]*?\)<\/span>)*$/;
|
const onlyMathRegex = /^<span class="math-tex">\\\([^\)]*?\)<\/span>(?:<span class="math-tex">\\\([^\)]*?\)<\/span>)*$/;
|
||||||
|
|
||||||
for (let match = null, hltIndex = 0; (match = combinedRegex.exec(content)) !== null; hltIndex++) {
|
for (let match: RegExpMatchArray | null = null, hltIndex = 0; (match = combinedRegex.exec(content)) !== null; hltIndex++) {
|
||||||
const subHtml = match[0];
|
const subHtml = match[0];
|
||||||
const startIndex = match.index;
|
const startIndex = match.index;
|
||||||
const endIndex = combinedRegex.lastIndex;
|
const endIndex = combinedRegex.lastIndex;
|
||||||
@ -324,8 +324,9 @@ export default class HighlightsListWidget extends RightPanelWidget {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const textEditor = await this.noteContext.getTextEditor();
|
const textEditor = await this.noteContext.getTextEditor();
|
||||||
if (textEditor) {
|
const el = textEditor?.editing.view.domRoots.values().next().value;
|
||||||
targetElement = $(textEditor.editing.view.domRoots.values().next().value)
|
if (el) {
|
||||||
|
targetElement = $(el)
|
||||||
.find(findSubStr)
|
.find(findSubStr)
|
||||||
.filter(function () {
|
.filter(function () {
|
||||||
// When finding span[style*="color"] but not looking for span[style*="background-color"],
|
// When finding span[style*="color"] but not looking for span[style*="background-color"],
|
||||||
|
@ -350,7 +350,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
},
|
},
|
||||||
scrollParent: this.$tree,
|
scrollParent: this.$tree,
|
||||||
minExpandLevel: 2, // root can't be collapsed
|
minExpandLevel: 2, // root can't be collapsed
|
||||||
click: (event: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent | React.PointerEvent<HTMLCanvasElement>, data): boolean => {
|
click: (event, data): boolean => {
|
||||||
this.activityDetected();
|
this.activityDetected();
|
||||||
|
|
||||||
const targetType = data.targetType;
|
const targetType = data.targetType;
|
||||||
@ -745,7 +745,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
prepareChildren(parentNote: FNote) {
|
prepareChildren(parentNote: FNote) {
|
||||||
utils.assertArguments(parentNote);
|
utils.assertArguments(parentNote);
|
||||||
|
|
||||||
const noteList = [];
|
const noteList: Node[] = [];
|
||||||
|
|
||||||
const hideArchivedNotes = this.hideArchivedNotes;
|
const hideArchivedNotes = this.hideArchivedNotes;
|
||||||
|
|
||||||
@ -839,7 +839,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
getExtraClasses(note: FNote) {
|
getExtraClasses(note: FNote) {
|
||||||
utils.assertArguments(note);
|
utils.assertArguments(note);
|
||||||
|
|
||||||
const extraClasses = [];
|
const extraClasses: string[] = [];
|
||||||
|
|
||||||
if (note.isProtected) {
|
if (note.isProtected) {
|
||||||
extraClasses.push("protected");
|
extraClasses.push("protected");
|
||||||
@ -1265,8 +1265,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
const allBranchesDeleted = branchRows.every((branchRow) => !!branchRow.isDeleted);
|
const allBranchesDeleted = branchRows.every((branchRow) => !!branchRow.isDeleted);
|
||||||
|
|
||||||
// activeNode is supposed to be moved when we find out activeNode is deleted but not all branches are deleted. save it for fixing activeNodePath after all nodes loaded.
|
// activeNode is supposed to be moved when we find out activeNode is deleted but not all branches are deleted. save it for fixing activeNodePath after all nodes loaded.
|
||||||
let movedActiveNode = null;
|
let movedActiveNode: Fancytree.FancytreeNode | null = null;
|
||||||
let parentsOfAddedNodes = [];
|
let parentsOfAddedNodes: Fancytree.FancytreeNode[] = [];
|
||||||
|
|
||||||
for (const branchRow of branchRows) {
|
for (const branchRow of branchRows) {
|
||||||
if (branchRow.noteId) {
|
if (branchRow.noteId) {
|
||||||
|
@ -85,7 +85,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
|||||||
this.$notePathIntro.text(t("note_paths.intro_not_placed"));
|
this.$notePathIntro.text(t("note_paths.intro_not_placed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderedPaths = [];
|
const renderedPaths: JQuery<HTMLElement>[] = [];
|
||||||
|
|
||||||
for (const notePathRecord of sortedNotePaths) {
|
for (const notePathRecord of sortedNotePaths) {
|
||||||
const notePath = notePathRecord.notePath;
|
const notePath = notePathRecord.notePath;
|
||||||
@ -100,7 +100,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
|||||||
const $pathItem = $("<li>");
|
const $pathItem = $("<li>");
|
||||||
const pathSegments: string[] = [];
|
const pathSegments: string[] = [];
|
||||||
const lastIndex = notePath.length - 1;
|
const lastIndex = notePath.length - 1;
|
||||||
|
|
||||||
for (let i = 0; i < notePath.length; i++) {
|
for (let i = 0; i < notePath.length; i++) {
|
||||||
const noteId = notePath[i];
|
const noteId = notePath[i];
|
||||||
pathSegments.push(noteId);
|
pathSegments.push(noteId);
|
||||||
@ -109,13 +109,13 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
$noteLink.find("a").addClass("no-tooltip-preview tn-link");
|
$noteLink.find("a").addClass("no-tooltip-preview tn-link");
|
||||||
$pathItem.append($noteLink);
|
$pathItem.append($noteLink);
|
||||||
|
|
||||||
if (i != lastIndex) {
|
if (i != lastIndex) {
|
||||||
$pathItem.append(" / ");
|
$pathItem.append(" / ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const icons = [];
|
const icons: string[] = [];
|
||||||
|
|
||||||
if (this.notePath === notePath.join("/")) {
|
if (this.notePath === notePath.join("/")) {
|
||||||
$pathItem.addClass("path-current");
|
$pathItem.addClass("path-current");
|
||||||
|
@ -122,7 +122,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const $cells = [];
|
const $cells: JQuery<HTMLElement>[] = [];
|
||||||
|
|
||||||
for (const definitionAttr of promotedDefAttrs) {
|
for (const definitionAttr of promotedDefAttrs) {
|
||||||
const valueType = definitionAttr.name.startsWith("label:") ? "label" : "relation";
|
const valueType = definitionAttr.name.startsWith("label:") ? "label" : "relation";
|
||||||
|
@ -208,7 +208,7 @@ const TAB_ROW_TPL = `
|
|||||||
color: var(--active-tab-text-color);
|
color: var(--active-tab-text-color);
|
||||||
box-shadow: inset -1px 0 0 0 var(--main-border-color);
|
box-shadow: inset -1px 0 0 0 var(--main-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-scroll-button-right {
|
.tab-scroll-button-right {
|
||||||
color: var(--active-tab-text-color);
|
color: var(--active-tab-text-color);
|
||||||
box-shadow: inset 1px 0 0 0 var(--main-border-color);
|
box-shadow: inset 1px 0 0 0 var(--main-border-color);
|
||||||
@ -279,7 +279,7 @@ const TAB_ROW_TPL = `
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-row-widget-scrolling-container {
|
.tab-row-widget-scrolling-container {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
@ -287,9 +287,9 @@ const TAB_ROW_TPL = `
|
|||||||
}
|
}
|
||||||
/* Chrome/Safari */
|
/* Chrome/Safari */
|
||||||
.tab-row-widget-scrolling-container::-webkit-scrollbar {
|
.tab-row-widget-scrolling-container::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<div class="tab-scroll-button-left bx bx-chevron-left"></div>
|
<div class="tab-scroll-button-left bx bx-chevron-left"></div>
|
||||||
<div class="tab-row-widget-scrolling-container">
|
<div class="tab-row-widget-scrolling-container">
|
||||||
|
@ -64,7 +64,7 @@ const TPL = /*html*/`<div class="toc-widget">
|
|||||||
}
|
}
|
||||||
|
|
||||||
.toc > ol {
|
.toc > ol {
|
||||||
--toc-depth-level: 1;
|
--toc-depth-level: 1;
|
||||||
}
|
}
|
||||||
.toc > ol > ol {
|
.toc > ol > ol {
|
||||||
--toc-depth-level: 2;
|
--toc-depth-level: 2;
|
||||||
@ -84,7 +84,7 @@ const TPL = /*html*/`<div class="toc-widget">
|
|||||||
}
|
}
|
||||||
|
|
||||||
.toc li {
|
.toc li {
|
||||||
padding-left: calc((var(--toc-depth-level) - 1) * 20px + 4px);
|
padding-left: calc((var(--toc-depth-level) - 1) * 20px + 4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc li .collapse-button {
|
.toc li .collapse-button {
|
||||||
@ -304,7 +304,7 @@ export default class TocWidget extends RightPanelWidget {
|
|||||||
const validHeadingKeys = new Set<string>(); // Used to clean up obsolete entries in tocCollapsedHeadings
|
const validHeadingKeys = new Set<string>(); // Used to clean up obsolete entries in tocCollapsedHeadings
|
||||||
|
|
||||||
let headingCount = 0;
|
let headingCount = 0;
|
||||||
for (let m = null, headingIndex = 0; (m = headingTagsRegex.exec(html)) !== null; headingIndex++) {
|
for (let m: RegExpMatchArray | null = null, headingIndex = 0; (m = headingTagsRegex.exec(html)) !== null; headingIndex++) {
|
||||||
//
|
//
|
||||||
// Nest/unnest whatever necessary number of ordered lists
|
// Nest/unnest whatever necessary number of ordered lists
|
||||||
//
|
//
|
||||||
@ -394,12 +394,14 @@ export default class TocWidget extends RightPanelWidget {
|
|||||||
const isDocNote = this.note.type === "doc";
|
const isDocNote = this.note.type === "doc";
|
||||||
const isReadOnly = await this.noteContext.isReadOnly();
|
const isReadOnly = await this.noteContext.isReadOnly();
|
||||||
|
|
||||||
let $container;
|
let $container: JQuery<HTMLElement> | null = null;
|
||||||
if (isReadOnly || isDocNote) {
|
if (isReadOnly || isDocNote) {
|
||||||
$container = await this.noteContext.getContentElement();
|
$container = await this.noteContext.getContentElement();
|
||||||
} else {
|
} else {
|
||||||
const textEditor = await this.noteContext.getTextEditor();
|
const textEditor = await this.noteContext.getTextEditor();
|
||||||
$container = $(textEditor.sourceElement);
|
if (textEditor?.sourceElement) {
|
||||||
|
$container = $(textEditor.sourceElement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const headingElement = $container?.find(":header:not(section.include-note :header)")?.[headingIndex];
|
const headingElement = $container?.find(":header:not(section.include-note :header)")?.[headingIndex];
|
||||||
|
@ -138,8 +138,8 @@ export default abstract class AbstractSvgSplitTypeWidget extends AbstractSplitTy
|
|||||||
*/
|
*/
|
||||||
async #setupPanZoom(preservePanZoom: boolean) {
|
async #setupPanZoom(preservePanZoom: boolean) {
|
||||||
// Clean up
|
// Clean up
|
||||||
let pan = null;
|
let pan: SvgPanZoom.Point | null = null;
|
||||||
let zoom = null;
|
let zoom: number | null = null;
|
||||||
if (preservePanZoom && this.zoomInstance) {
|
if (preservePanZoom && this.zoomInstance) {
|
||||||
// Store pan and zoom for same note, when the user is editing the note.
|
// Store pan and zoom for same note, when the user is editing the note.
|
||||||
pan = this.zoomInstance.getPan();
|
pan = this.zoomInstance.getPan();
|
||||||
|
@ -6,7 +6,7 @@ type ToolbarConfig = string | "|" | { items: ToolbarConfig[] };
|
|||||||
describe("CKEditor config", () => {
|
describe("CKEditor config", () => {
|
||||||
it("has same toolbar items for fixed and floating", () => {
|
it("has same toolbar items for fixed and floating", () => {
|
||||||
function traverseItems(config: ToolbarConfig): string[] {
|
function traverseItems(config: ToolbarConfig): string[] {
|
||||||
const result = [];
|
const result: (string | string[])[] = [];
|
||||||
if (typeof config === "object") {
|
if (typeof config === "object") {
|
||||||
for (const item of config.items) {
|
for (const item of config.items) {
|
||||||
result.push(traverseItems(item));
|
result.push(traverseItems(item));
|
||||||
|
@ -111,7 +111,7 @@ export function buildConfig(): EditorConfig {
|
|||||||
},
|
},
|
||||||
mapLanguageName: getHighlightJsNameForMime,
|
mapLanguageName: getHighlightJsNameForMime,
|
||||||
defaultMimeType: MIME_TYPE_AUTO,
|
defaultMimeType: MIME_TYPE_AUTO,
|
||||||
enabled: isSyntaxHighlightEnabled
|
enabled: isSyntaxHighlightEnabled()
|
||||||
},
|
},
|
||||||
clipboard: {
|
clipboard: {
|
||||||
copy: copyText
|
copy: copyText
|
||||||
@ -134,7 +134,7 @@ export function buildToolbarConfig(isClassicToolbar: boolean) {
|
|||||||
|
|
||||||
export function buildMobileToolbar() {
|
export function buildMobileToolbar() {
|
||||||
const classicConfig = buildClassicToolbar(false);
|
const classicConfig = buildClassicToolbar(false);
|
||||||
const items = [];
|
const items: string[] = [];
|
||||||
|
|
||||||
for (const item of classicConfig.toolbar.items) {
|
for (const item of classicConfig.toolbar.items) {
|
||||||
if (typeof item === "object" && "items" in item) {
|
if (typeof item === "object" && "items" in item) {
|
||||||
|
@ -589,7 +589,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
backgroundColor: buildSelectedBackgroundColor(editor.commands.get(command)?.value as boolean)
|
backgroundColor: buildSelectedBackgroundColor(editor.commands.get(command)?.value as boolean)
|
||||||
});
|
});
|
||||||
|
|
||||||
let headingSelectedIndex = undefined;
|
let headingSelectedIndex: number | undefined = undefined;
|
||||||
const headingCommand = editor.commands.get("heading");
|
const headingCommand = editor.commands.get("heading");
|
||||||
const paragraphCommand = editor.commands.get("paragraph");
|
const paragraphCommand = editor.commands.get("paragraph");
|
||||||
if (paragraphCommand?.value) {
|
if (paragraphCommand?.value) {
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
|
||||||
import { trimIndentation } from "@triliumnext/commons";
|
|
||||||
import { validateMermaid } from "./mermaid.js";
|
|
||||||
|
|
||||||
describe("Mermaid linter", () => {
|
|
||||||
|
|
||||||
(global as any).CodeMirror = {
|
|
||||||
Pos(line: number, col: number) {
|
|
||||||
return { line, col };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
it("reports correctly bad diagram type", async () => {
|
|
||||||
const input = trimIndentation`\
|
|
||||||
stateDiagram-v23
|
|
||||||
[*] -> Still
|
|
||||||
`;
|
|
||||||
|
|
||||||
const result = await validateMermaid(input);
|
|
||||||
expect(result).toMatchObject([{
|
|
||||||
message: "Expecting 'SPACE', 'NL', 'SD', got 'ID'",
|
|
||||||
from: { line: 0, col: 0 },
|
|
||||||
to: { line: 0, col: 1 }
|
|
||||||
}]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("reports correctly basic arrow missing in diagram", async () => {
|
|
||||||
const input = trimIndentation`\
|
|
||||||
xychart-beta horizontal
|
|
||||||
title "Percentage usge"
|
|
||||||
x-axis [data, sys, usr, var]
|
|
||||||
y-axis 0--->100
|
|
||||||
bar [20, 70, 0, 0]
|
|
||||||
`;
|
|
||||||
|
|
||||||
const result = await validateMermaid(input);
|
|
||||||
expect(result).toMatchObject([{
|
|
||||||
message: "Expecting 'ARROW_DELIMITER', got 'MINUS'",
|
|
||||||
from: { line: 3, col: 8 },
|
|
||||||
to: { line: 3, col: 9 }
|
|
||||||
}]);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,58 +0,0 @@
|
|||||||
import mermaid from "mermaid";
|
|
||||||
|
|
||||||
interface MermaidParseError extends Error {
|
|
||||||
hash: {
|
|
||||||
text: string;
|
|
||||||
token: string;
|
|
||||||
line: number;
|
|
||||||
loc: {
|
|
||||||
first_line: number;
|
|
||||||
first_column: number;
|
|
||||||
last_line: number;
|
|
||||||
last_column: number;
|
|
||||||
};
|
|
||||||
expected: string[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function registerErrorReporter() {
|
|
||||||
CodeMirror.registerHelper("lint", null, validateMermaid);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function validateMermaid(text: string) {
|
|
||||||
if (!text.trim()) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await mermaid.parse(text);
|
|
||||||
} catch (e: unknown) {
|
|
||||||
console.warn("Got validation error", JSON.stringify(e));
|
|
||||||
|
|
||||||
const mermaidError = (e as MermaidParseError);
|
|
||||||
const loc = mermaidError.hash.loc;
|
|
||||||
|
|
||||||
let firstCol = loc.first_column + 1;
|
|
||||||
let lastCol = loc.last_column + 1;
|
|
||||||
|
|
||||||
if (firstCol === 1 && lastCol === 1) {
|
|
||||||
firstCol = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let messageLines = mermaidError.message.split("\n");
|
|
||||||
if (messageLines.length >= 4) {
|
|
||||||
messageLines = messageLines.slice(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
message: messageLines.join("\n"),
|
|
||||||
severity: "error",
|
|
||||||
from: CodeMirror.Pos(loc.first_line - 1, firstCol),
|
|
||||||
to: CodeMirror.Pos(loc.last_line - 1, lastCol)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
@ -207,8 +207,8 @@ export default class MindMapWidget extends TypeWidget {
|
|||||||
await this.#initLibrary(content?.direction);
|
await this.#initLibrary(content?.direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mind.refresh(content ?? this.MindElixir.new(NEW_TOPIC_NAME));
|
this.mind!.refresh(content ?? this.MindElixir.new(NEW_TOPIC_NAME));
|
||||||
this.mind.toCenter();
|
this.mind!.toCenter();
|
||||||
}
|
}
|
||||||
|
|
||||||
async #initLibrary(direction?: number) {
|
async #initLibrary(direction?: number) {
|
||||||
@ -259,7 +259,7 @@ export default class MindMapWidget extends TypeWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async renderSvg() {
|
async renderSvg() {
|
||||||
return await this.mind.exportSvg().text();
|
return await this.mind!.exportSvg().text();
|
||||||
}
|
}
|
||||||
|
|
||||||
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||||
|
@ -198,7 +198,7 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
const providerPrecedence = (this.$widget.find('.ai-provider-precedence').val() as string || '').split(',');
|
const providerPrecedence = (this.$widget.find('.ai-provider-precedence').val() as string || '').split(',');
|
||||||
|
|
||||||
// Check for OpenAI configuration if it's in the precedence list
|
// Check for OpenAI configuration if it's in the precedence list
|
||||||
const openaiWarnings = [];
|
const openaiWarnings: string[] = [];
|
||||||
if (providerPrecedence.includes('openai')) {
|
if (providerPrecedence.includes('openai')) {
|
||||||
const openaiApiKey = this.$widget.find('.openai-api-key').val();
|
const openaiApiKey = this.$widget.find('.openai-api-key').val();
|
||||||
if (!openaiApiKey) {
|
if (!openaiApiKey) {
|
||||||
@ -207,7 +207,7 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for Anthropic configuration if it's in the precedence list
|
// Check for Anthropic configuration if it's in the precedence list
|
||||||
const anthropicWarnings = [];
|
const anthropicWarnings: string[] = [];
|
||||||
if (providerPrecedence.includes('anthropic')) {
|
if (providerPrecedence.includes('anthropic')) {
|
||||||
const anthropicApiKey = this.$widget.find('.anthropic-api-key').val();
|
const anthropicApiKey = this.$widget.find('.anthropic-api-key').val();
|
||||||
if (!anthropicApiKey) {
|
if (!anthropicApiKey) {
|
||||||
@ -216,7 +216,7 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for Voyage configuration if it's in the precedence list
|
// Check for Voyage configuration if it's in the precedence list
|
||||||
const voyageWarnings = [];
|
const voyageWarnings: string[] = [];
|
||||||
if (providerPrecedence.includes('voyage')) {
|
if (providerPrecedence.includes('voyage')) {
|
||||||
const voyageApiKey = this.$widget.find('.voyage-api-key').val();
|
const voyageApiKey = this.$widget.find('.voyage-api-key').val();
|
||||||
if (!voyageApiKey) {
|
if (!voyageApiKey) {
|
||||||
@ -225,7 +225,7 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for Ollama configuration if it's in the precedence list
|
// Check for Ollama configuration if it's in the precedence list
|
||||||
const ollamaWarnings = [];
|
const ollamaWarnings: string[] = [];
|
||||||
if (providerPrecedence.includes('ollama')) {
|
if (providerPrecedence.includes('ollama')) {
|
||||||
const ollamaBaseUrl = this.$widget.find('.ollama-base-url').val();
|
const ollamaBaseUrl = this.$widget.find('.ollama-base-url').val();
|
||||||
if (!ollamaBaseUrl) {
|
if (!ollamaBaseUrl) {
|
||||||
@ -234,7 +234,7 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Similar checks for embeddings
|
// Similar checks for embeddings
|
||||||
const embeddingWarnings = [];
|
const embeddingWarnings: string[] = [];
|
||||||
const embeddingsEnabled = this.$widget.find('.enable-automatic-indexing').prop('checked');
|
const embeddingsEnabled = this.$widget.find('.enable-automatic-indexing').prop('checked');
|
||||||
|
|
||||||
if (embeddingsEnabled) {
|
if (embeddingsEnabled) {
|
||||||
|
@ -83,6 +83,13 @@ interface CreateChildResponse {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Event {
|
||||||
|
startDate: string,
|
||||||
|
endDate?: string | null,
|
||||||
|
startTime?: string | null,
|
||||||
|
endTime?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
const CALENDAR_VIEWS = [
|
const CALENDAR_VIEWS = [
|
||||||
"timeGridWeek",
|
"timeGridWeek",
|
||||||
"dayGridMonth",
|
"dayGridMonth",
|
||||||
@ -325,8 +332,8 @@ export default class CalendarView extends ViewMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#parseStartEndTimeFromEvent(e: DateSelectArg | EventImpl) {
|
#parseStartEndTimeFromEvent(e: DateSelectArg | EventImpl) {
|
||||||
let startTime = null;
|
let startTime: string | undefined | null = null;
|
||||||
let endTime = null;
|
let endTime: string | undefined | null = null;
|
||||||
if (!e.allDay) {
|
if (!e.allDay) {
|
||||||
startTime = CalendarView.#formatTimeToLocalISO(e.start);
|
startTime = CalendarView.#formatTimeToLocalISO(e.start);
|
||||||
endTime = CalendarView.#formatTimeToLocalISO(e.end);
|
endTime = CalendarView.#formatTimeToLocalISO(e.end);
|
||||||
@ -391,7 +398,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async #buildEventsForCalendar(e: EventSourceFuncArg) {
|
async #buildEventsForCalendar(e: EventSourceFuncArg) {
|
||||||
const events = [];
|
const events: EventInput[] = [];
|
||||||
|
|
||||||
// Gather all the required date note IDs.
|
// Gather all the required date note IDs.
|
||||||
const dateRange = utils.getMonthsInDateRange(e.startStr, e.endStr);
|
const dateRange = utils.getMonthsInDateRange(e.startStr, e.endStr);
|
||||||
@ -483,12 +490,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
return note.getLabelValue(defaultLabelName);
|
return note.getLabelValue(defaultLabelName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async buildEvent(note: FNote, { startDate, endDate, startTime, endTime }: {
|
static async buildEvent(note: FNote, { startDate, endDate, startTime, endTime }: Event) {
|
||||||
startDate: string,
|
|
||||||
endDate?: string | null,
|
|
||||||
startTime?: string | null,
|
|
||||||
endTime?: string | null
|
|
||||||
}) {
|
|
||||||
const customTitleAttributeName = note.getLabelValue("calendar:title");
|
const customTitleAttributeName = note.getLabelValue("calendar:title");
|
||||||
const titles = await CalendarView.#parseCustomTitle(customTitleAttributeName, note);
|
const titles = await CalendarView.#parseCustomTitle(customTitleAttributeName, note);
|
||||||
const color = note.getLabelValue("calendar:color") ?? note.getLabelValue("color");
|
const color = note.getLabelValue("calendar:color") ?? note.getLabelValue("color");
|
||||||
@ -553,7 +555,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
if (relations.length > 0) {
|
if (relations.length > 0) {
|
||||||
const noteIds = relations.map((r) => r.targetNoteId);
|
const noteIds = relations.map((r) => r.targetNoteId);
|
||||||
const notesFromRelation = await froca.getNotes(noteIds);
|
const notesFromRelation = await froca.getNotes(noteIds);
|
||||||
const titles = [];
|
const titles: string[][] = [];
|
||||||
|
|
||||||
for (const targetNote of notesFromRelation) {
|
for (const targetNote of notesFromRelation) {
|
||||||
const targetCustomTitleValue = targetNote.getAttributeValue("label", "calendar:title");
|
const targetCustomTitleValue = targetNote.getAttributeValue("label", "calendar:title");
|
||||||
@ -631,7 +633,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
|
|
||||||
// Icon button.
|
// Icon button.
|
||||||
const iconEl = subItem.querySelector("span.fc-icon");
|
const iconEl = subItem.querySelector("span.fc-icon");
|
||||||
let icon = null;
|
let icon: string | null = null;
|
||||||
if (iconEl?.classList.contains("fc-icon-chevron-left")) {
|
if (iconEl?.classList.contains("fc-icon-chevron-left")) {
|
||||||
icon = "NSImageNameTouchBarGoBackTemplate";
|
icon = "NSImageNameTouchBarGoBackTemplate";
|
||||||
mode = "buttons";
|
mode = "buttons";
|
||||||
|
@ -6,8 +6,7 @@
|
|||||||
"node"
|
"node"
|
||||||
],
|
],
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo",
|
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo"
|
||||||
"verbatimModuleSyntax": false
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts"
|
"src/**/*.ts"
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"strict": false,
|
|
||||||
"types": [
|
"types": [
|
||||||
"node",
|
"node",
|
||||||
"express"
|
"express"
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"outDir": "out-tsc/playwright",
|
"outDir": "out-tsc/playwright",
|
||||||
"sourceMap": false,
|
"sourceMap": false
|
||||||
"verbatimModuleSyntax": false
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
|
@ -251,7 +251,7 @@ export default class Becca {
|
|||||||
getAllNoteSet() {
|
getAllNoteSet() {
|
||||||
// caching this since it takes 10s of milliseconds to fill this initial NoteSet for many notes
|
// caching this since it takes 10s of milliseconds to fill this initial NoteSet for many notes
|
||||||
if (!this.allNoteSetCache) {
|
if (!this.allNoteSetCache) {
|
||||||
const allNotes = [];
|
const allNotes: BNote[] = [];
|
||||||
|
|
||||||
for (const noteId in this.notes) {
|
for (const noteId in this.notes) {
|
||||||
const note = this.notes[noteId];
|
const note = this.notes[noteId];
|
||||||
|
@ -76,7 +76,7 @@ function getNoteTitleArrayForPath(notePathArray: string[]) {
|
|||||||
return [getNoteTitle(notePathArray[0])];
|
return [getNoteTitle(notePathArray[0])];
|
||||||
}
|
}
|
||||||
|
|
||||||
const titles = [];
|
const titles: string[] = [];
|
||||||
|
|
||||||
let parentNoteId = "root";
|
let parentNoteId = "root";
|
||||||
let hoistedNotePassed = false;
|
let hoistedNotePassed = false;
|
||||||
|
@ -388,7 +388,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const templateAttributes = [];
|
const templateAttributes: BAttribute[] = [];
|
||||||
|
|
||||||
for (const ownedAttr of parentAttributes) {
|
for (const ownedAttr of parentAttributes) {
|
||||||
// parentAttributes so we process also inherited templates
|
// parentAttributes so we process also inherited templates
|
||||||
|
@ -254,7 +254,7 @@ function hasConnectingRelation(sourceNote: BNote, targetNote: BNote) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function findSimilarNotes(noteId: string): Promise<SimilarNote[] | undefined> {
|
async function findSimilarNotes(noteId: string): Promise<SimilarNote[] | undefined> {
|
||||||
const results = [];
|
const results: SimilarNote[] = [];
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
const baseNote = becca.notes[noteId];
|
const baseNote = becca.notes[noteId];
|
||||||
|
@ -12,6 +12,11 @@ interface Backlink {
|
|||||||
excerpts?: string[];
|
excerpts?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TreeLink {
|
||||||
|
sourceNoteId: string;
|
||||||
|
targetNoteId: string;
|
||||||
|
}
|
||||||
|
|
||||||
function buildDescendantCountMap(noteIdsToCount: string[]) {
|
function buildDescendantCountMap(noteIdsToCount: string[]) {
|
||||||
if (!Array.isArray(noteIdsToCount)) {
|
if (!Array.isArray(noteIdsToCount)) {
|
||||||
throw new Error("noteIdsToCount: type error");
|
throw new Error("noteIdsToCount: type error");
|
||||||
@ -50,7 +55,7 @@ function getNeighbors(note: BNote, depth: number): string[] {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const retNoteIds = [];
|
const retNoteIds: string[] = [];
|
||||||
|
|
||||||
function isIgnoredRelation(relation: BAttribute) {
|
function isIgnoredRelation(relation: BAttribute) {
|
||||||
return ["relationMapLink", "template", "inherit", "image", "ancestor"].includes(relation.name);
|
return ["relationMapLink", "template", "inherit", "image", "ancestor"].includes(relation.name);
|
||||||
@ -196,7 +201,7 @@ function getTreeMap(req: Request) {
|
|||||||
const noteIds = new Set<string>();
|
const noteIds = new Set<string>();
|
||||||
notes.forEach(([noteId]) => noteId && noteIds.add(noteId));
|
notes.forEach(([noteId]) => noteId && noteIds.add(noteId));
|
||||||
|
|
||||||
const links = [];
|
const links: TreeLink[] = [];
|
||||||
|
|
||||||
for (const { parentNoteId, childNoteId } of subtree.relationships) {
|
for (const { parentNoteId, childNoteId } of subtree.relationships) {
|
||||||
if (!noteIds.has(parentNoteId) || !noteIds.has(childNoteId)) {
|
if (!noteIds.has(parentNoteId) || !noteIds.has(childNoteId)) {
|
||||||
@ -246,7 +251,7 @@ function findExcerpts(sourceNote: BNote, referencedNoteId: string) {
|
|||||||
const html = sourceNote.getContent();
|
const html = sourceNote.getContent();
|
||||||
const document = new JSDOM(html).window.document;
|
const document = new JSDOM(html).window.document;
|
||||||
|
|
||||||
const excerpts = [];
|
const excerpts: string[] = [];
|
||||||
|
|
||||||
removeImages(document);
|
removeImages(document);
|
||||||
|
|
||||||
|
@ -9,6 +9,12 @@ import { changeLanguage, getLocales } from "../../services/i18n.js";
|
|||||||
import type { OptionNames } from "@triliumnext/commons";
|
import type { OptionNames } from "@triliumnext/commons";
|
||||||
import config from "../../services/config.js";
|
import config from "../../services/config.js";
|
||||||
|
|
||||||
|
interface UserTheme {
|
||||||
|
val: string; // value of the theme, used in the URL
|
||||||
|
title: string; // title of the theme, displayed in the UI
|
||||||
|
noteId: string; // ID of the note containing the theme
|
||||||
|
}
|
||||||
|
|
||||||
// options allowed to be updated directly in the Options dialog
|
// options allowed to be updated directly in the Options dialog
|
||||||
const ALLOWED_OPTIONS = new Set<OptionNames>([
|
const ALLOWED_OPTIONS = new Set<OptionNames>([
|
||||||
"eraseEntitiesAfterTimeInSeconds",
|
"eraseEntitiesAfterTimeInSeconds",
|
||||||
@ -177,7 +183,7 @@ function update(name: string, value: string) {
|
|||||||
|
|
||||||
function getUserThemes() {
|
function getUserThemes() {
|
||||||
const notes = searchService.searchNotes("#appTheme", { ignoreHoistedNote: true });
|
const notes = searchService.searchNotes("#appTheme", { ignoreHoistedNote: true });
|
||||||
const ret = [];
|
const ret: UserTheme[] = [];
|
||||||
|
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
let value = note.getOwnedLabelValue("appTheme");
|
let value = note.getOwnedLabelValue("appTheme");
|
||||||
|
@ -21,7 +21,7 @@ interface RecentChangeRow {
|
|||||||
function getRecentChanges(req: Request) {
|
function getRecentChanges(req: Request) {
|
||||||
const { ancestorNoteId } = req.params;
|
const { ancestorNoteId } = req.params;
|
||||||
|
|
||||||
let recentChanges = [];
|
let recentChanges: RecentChangeRow[] = [];
|
||||||
|
|
||||||
const revisionRows = sql.getRows<RecentChangeRow>(`
|
const revisionRows = sql.getRows<RecentChangeRow>(`
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import scriptService from "../../services/script.js";
|
import scriptService, { type Bundle } from "../../services/script.js";
|
||||||
import attributeService from "../../services/attributes.js";
|
import attributeService from "../../services/attributes.js";
|
||||||
import becca from "../../becca/becca.js";
|
import becca from "../../becca/becca.js";
|
||||||
import syncService from "../../services/sync.js";
|
import syncService from "../../services/sync.js";
|
||||||
@ -54,7 +54,7 @@ function run(req: Request) {
|
|||||||
function getBundlesWithLabel(label: string, value?: string) {
|
function getBundlesWithLabel(label: string, value?: string) {
|
||||||
const notes = attributeService.getNotesWithLabel(label, value);
|
const notes = attributeService.getNotesWithLabel(label, value);
|
||||||
|
|
||||||
const bundles = [];
|
const bundles: Bundle[] = [];
|
||||||
|
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
const bundle = scriptService.getScriptBundleForFrontend(note);
|
const bundle = scriptService.getScriptBundleForFrontend(note);
|
||||||
@ -97,7 +97,7 @@ function getRelationBundles(req: Request) {
|
|||||||
const targetNoteIds = filtered.map((relation) => relation.value);
|
const targetNoteIds = filtered.map((relation) => relation.value);
|
||||||
const uniqueNoteIds = Array.from(new Set(targetNoteIds));
|
const uniqueNoteIds = Array.from(new Set(targetNoteIds));
|
||||||
|
|
||||||
const bundles = [];
|
const bundles: Bundle[] = [];
|
||||||
|
|
||||||
for (const noteId of uniqueNoteIds) {
|
for (const noteId of uniqueNoteIds) {
|
||||||
const note = becca.getNoteOrThrow(noteId);
|
const note = becca.getNoteOrThrow(noteId);
|
||||||
|
@ -6,9 +6,14 @@ import type { Request } from "express";
|
|||||||
import ValidationError from "../../errors/validation_error.js";
|
import ValidationError from "../../errors/validation_error.js";
|
||||||
import { safeExtractMessageAndStackFromError } from "../../services/utils.js";
|
import { safeExtractMessageAndStackFromError } from "../../services/utils.js";
|
||||||
|
|
||||||
|
interface Table {
|
||||||
|
name: string;
|
||||||
|
columns: unknown[];
|
||||||
|
}
|
||||||
|
|
||||||
function getSchema() {
|
function getSchema() {
|
||||||
const tableNames = sql.getColumn(/*sql*/`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`);
|
const tableNames = sql.getColumn<string>(/*sql*/`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`);
|
||||||
const tables = [];
|
const tables: Table[] = [];
|
||||||
|
|
||||||
for (const tableName of tableNames) {
|
for (const tableName of tableNames) {
|
||||||
tables.push({
|
tables.push({
|
||||||
@ -31,7 +36,7 @@ function execute(req: Request) {
|
|||||||
const queries = content.split("\n---");
|
const queries = content.split("\n---");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const results = [];
|
const results: unknown[] = [];
|
||||||
|
|
||||||
for (let query of queries) {
|
for (let query of queries) {
|
||||||
query = query.trim();
|
query = query.trim();
|
||||||
|
@ -5,6 +5,7 @@ import log from "../../services/log.js";
|
|||||||
import NotFoundError from "../../errors/not_found_error.js";
|
import NotFoundError from "../../errors/not_found_error.js";
|
||||||
import type { Request } from "express";
|
import type { Request } from "express";
|
||||||
import type BNote from "../../becca/entities/bnote.js";
|
import type BNote from "../../becca/entities/bnote.js";
|
||||||
|
import type { AttributeRow, BranchRow, NoteRow } from "@triliumnext/commons";
|
||||||
|
|
||||||
function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set<string>) {
|
function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set<string>) {
|
||||||
const noteIds = new Set(_noteIds);
|
const noteIds = new Set(_noteIds);
|
||||||
@ -53,7 +54,7 @@ function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set<string>) {
|
|||||||
collectEntityIds(note);
|
collectEntityIds(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
const notes = [];
|
const notes: NoteRow[] = [];
|
||||||
|
|
||||||
for (const noteId of collectedNoteIds) {
|
for (const noteId of collectedNoteIds) {
|
||||||
const note = becca.notes[noteId];
|
const note = becca.notes[noteId];
|
||||||
@ -68,7 +69,7 @@ function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set<string>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const branches = [];
|
const branches: BranchRow[] = [];
|
||||||
|
|
||||||
if (noteIds.has("root")) {
|
if (noteIds.has("root")) {
|
||||||
branches.push({
|
branches.push({
|
||||||
@ -99,7 +100,7 @@ function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set<string>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const attributes = [];
|
const attributes: AttributeRow[] = [];
|
||||||
|
|
||||||
for (const attributeId of collectedAttributeIds) {
|
for (const attributeId of collectedAttributeIds) {
|
||||||
const attribute = becca.attributes[attributeId];
|
const attribute = becca.attributes[attributeId];
|
||||||
|
@ -7,6 +7,11 @@ import eraseService from "./erase.js";
|
|||||||
|
|
||||||
type SectorHash = Record<string, string>;
|
type SectorHash = Record<string, string>;
|
||||||
|
|
||||||
|
interface FailedCheck {
|
||||||
|
entityName: string;
|
||||||
|
sector: string[1];
|
||||||
|
}
|
||||||
|
|
||||||
function getEntityHashes() {
|
function getEntityHashes() {
|
||||||
// blob erasure is not synced, we should check before each sync if there's some blob to erase
|
// blob erasure is not synced, we should check before each sync if there's some blob to erase
|
||||||
eraseService.eraseUnusedBlobs();
|
eraseService.eraseUnusedBlobs();
|
||||||
@ -56,7 +61,7 @@ function getEntityHashes() {
|
|||||||
|
|
||||||
function checkContentHashes(otherHashes: Record<string, SectorHash>) {
|
function checkContentHashes(otherHashes: Record<string, SectorHash>) {
|
||||||
const entityHashes = getEntityHashes();
|
const entityHashes = getEntityHashes();
|
||||||
const failedChecks = [];
|
const failedChecks: FailedCheck[] = [];
|
||||||
|
|
||||||
for (const entityName in entityHashes) {
|
for (const entityName in entityHashes) {
|
||||||
const thisSectorHashes: SectorHash = entityHashes[entityName] || {};
|
const thisSectorHashes: SectorHash = entityHashes[entityName] || {};
|
||||||
|
@ -51,7 +51,7 @@ export function getLocales(): Locale[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentLanguage(): LOCALE_IDS {
|
function getCurrentLanguage(): LOCALE_IDS {
|
||||||
let language: string;
|
let language: string | null = null;
|
||||||
if (sql_init.isDbInitialized()) {
|
if (sql_init.isDbInitialized()) {
|
||||||
language = options.getOptionOrNull("locale");
|
language = options.getOptionOrNull("locale");
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ async function importOpml(taskContext: TaskContext, fileBuffer: string | Buffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const outlines = xml.opml.body[0].outline || [];
|
const outlines = xml.opml.body[0].outline || [];
|
||||||
let returnNote = null;
|
let returnNote: BNote | null = null;
|
||||||
|
|
||||||
for (const outline of outlines) {
|
for (const outline of outlines) {
|
||||||
const note = importOutline(outline, parentNote.noteId);
|
const note = importOutline(outline, parentNote.noteId);
|
||||||
|
@ -9,7 +9,7 @@ export interface Message {
|
|||||||
content: string;
|
content: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
tool_call_id?: string;
|
tool_call_id?: string;
|
||||||
tool_calls?: ToolCall[];
|
tool_calls?: ToolCall[] | null;
|
||||||
sessionId?: string; // Optional session ID for WebSocket communication
|
sessionId?: string; // Optional session ID for WebSocket communication
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +210,7 @@ export interface ChatResponse {
|
|||||||
stream?: (callback: (chunk: StreamChunk) => Promise<void> | void) => Promise<string>;
|
stream?: (callback: (chunk: StreamChunk) => Promise<void> | void) => Promise<string>;
|
||||||
|
|
||||||
/** Tool calls from the LLM (if tools were used and the model supports them) */
|
/** Tool calls from the LLM (if tools were used and the model supports them) */
|
||||||
tool_calls?: ToolCall[];
|
tool_calls?: ToolCall[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AIService {
|
export interface AIService {
|
||||||
|
@ -160,7 +160,7 @@ function extractJsStructure(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for class declarations
|
// Look for class declarations
|
||||||
const classes = [];
|
const classes: string[] = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
if (line.startsWith('class ') || line.includes(' class ')) {
|
if (line.startsWith('class ') || line.includes(' class ')) {
|
||||||
@ -173,7 +173,7 @@ function extractJsStructure(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for function declarations
|
// Look for function declarations
|
||||||
const functions = [];
|
const functions: string[] = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
if (line.startsWith('function ') ||
|
if (line.startsWith('function ') ||
|
||||||
@ -212,7 +212,7 @@ function extractPythonStructure(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for class declarations
|
// Look for class declarations
|
||||||
const classes = [];
|
const classes: string[] = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
if (line.startsWith('class ')) {
|
if (line.startsWith('class ')) {
|
||||||
@ -225,7 +225,7 @@ function extractPythonStructure(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for function declarations
|
// Look for function declarations
|
||||||
const functions = [];
|
const functions: string[] = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
if (line.startsWith('def ')) {
|
if (line.startsWith('def ')) {
|
||||||
@ -263,7 +263,7 @@ function extractClassBasedStructure(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for class declarations
|
// Look for class declarations
|
||||||
const classes = [];
|
const classes: string[] = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
if (line.match(/^(public|private|protected)?\s*(class|interface|enum)\s+\w+/)) {
|
if (line.match(/^(public|private|protected)?\s*(class|interface|enum)\s+\w+/)) {
|
||||||
@ -276,7 +276,7 @@ function extractClassBasedStructure(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for method declarations
|
// Look for method declarations
|
||||||
const methods = [];
|
const methods: string[] = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
if (line.match(/^(public|private|protected)?\s*(static)?\s*[\w<>[\]]+\s+\w+\s*\(/)) {
|
if (line.match(/^(public|private|protected)?\s*(static)?\s*[\w<>[\]]+\s+\w+\s*\(/)) {
|
||||||
@ -319,7 +319,7 @@ function extractGoStructure(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for type declarations (structs, interfaces)
|
// Look for type declarations (structs, interfaces)
|
||||||
const types = [];
|
const types: string[] = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
if (line.startsWith('type ') && (line.includes(' struct ') || line.includes(' interface '))) {
|
if (line.startsWith('type ') && (line.includes(' struct ') || line.includes(' interface '))) {
|
||||||
@ -332,7 +332,7 @@ function extractGoStructure(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for function declarations
|
// Look for function declarations
|
||||||
const functions = [];
|
const functions: string[] = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
if (line.startsWith('func ')) {
|
if (line.startsWith('func ')) {
|
||||||
@ -366,7 +366,7 @@ function extractRustStructure(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for struct/enum/trait declarations
|
// Look for struct/enum/trait declarations
|
||||||
const types = [];
|
const types: string[] = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
if (line.startsWith('struct ') || line.startsWith('enum ') || line.startsWith('trait ')) {
|
if (line.startsWith('struct ') || line.startsWith('enum ') || line.startsWith('trait ')) {
|
||||||
@ -379,8 +379,8 @@ function extractRustStructure(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for function/impl declarations
|
// Look for function/impl declarations
|
||||||
const functions = [];
|
const functions: string[] = [];
|
||||||
const impls = [];
|
const impls: string[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
|
@ -198,7 +198,7 @@ export async function semanticChunking(
|
|||||||
|
|
||||||
// Try to split on headers first
|
// Try to split on headers first
|
||||||
const headerPattern = /#{1,6}\s+.+|<h[1-6][^>]*>.*?<\/h[1-6]>/g;
|
const headerPattern = /#{1,6}\s+.+|<h[1-6][^>]*>.*?<\/h[1-6]>/g;
|
||||||
const sections = [];
|
const sections: string[] = [];
|
||||||
|
|
||||||
let lastIndex = 0;
|
let lastIndex = 0;
|
||||||
let match;
|
let match;
|
||||||
|
@ -439,7 +439,7 @@ export class QueryDecompositionTool {
|
|||||||
|
|
||||||
// If no pattern match, try to extract noun phrases
|
// If no pattern match, try to extract noun phrases
|
||||||
const words = query.split(/\s+/);
|
const words = query.split(/\s+/);
|
||||||
const potentialEntities = [];
|
const potentialEntities: string[] = [];
|
||||||
let currentPhrase = '';
|
let currentPhrase = '';
|
||||||
|
|
||||||
for (const word of words) {
|
for (const word of words) {
|
||||||
|
@ -246,7 +246,7 @@ export abstract class BaseEmbeddingProvider {
|
|||||||
*/
|
*/
|
||||||
protected generateNoteContextText(context: NoteEmbeddingContext): string {
|
protected generateNoteContextText(context: NoteEmbeddingContext): string {
|
||||||
// Build a relationship-focused summary first
|
// Build a relationship-focused summary first
|
||||||
const relationshipSummary = [];
|
const relationshipSummary: string[] = [];
|
||||||
|
|
||||||
// Summarize the note's place in the hierarchy
|
// Summarize the note's place in the hierarchy
|
||||||
if (context.parentTitles.length > 0) {
|
if (context.parentTitles.length > 0) {
|
||||||
|
@ -22,20 +22,24 @@ export interface NoteEmbeddingContext {
|
|||||||
title: string;
|
title: string;
|
||||||
mime: string;
|
mime: string;
|
||||||
}[];
|
}[];
|
||||||
backlinks?: {
|
backlinks?: Backlink[];
|
||||||
sourceNoteId: string;
|
relatedNotes?: RelatedNote[];
|
||||||
sourceTitle: string;
|
|
||||||
relationName: string;
|
|
||||||
}[];
|
|
||||||
relatedNotes?: {
|
|
||||||
targetNoteId: string;
|
|
||||||
targetTitle: string;
|
|
||||||
relationName: string;
|
|
||||||
}[];
|
|
||||||
labelValues?: Record<string, string>;
|
labelValues?: Record<string, string>;
|
||||||
templateTitles?: string[];
|
templateTitles?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Backlink {
|
||||||
|
sourceNoteId: string;
|
||||||
|
sourceTitle: string;
|
||||||
|
relationName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RelatedNote {
|
||||||
|
targetNoteId: string;
|
||||||
|
targetTitle: string;
|
||||||
|
relationName: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about an embedding model's capabilities
|
* Information about an embedding model's capabilities
|
||||||
*/
|
*/
|
||||||
|
@ -13,6 +13,21 @@ import indexService from '../index_service.js';
|
|||||||
// Track which notes are currently being processed
|
// Track which notes are currently being processed
|
||||||
const notesInProcess = new Set<string>();
|
const notesInProcess = new Set<string>();
|
||||||
|
|
||||||
|
interface FailedItemRow {
|
||||||
|
noteId: string;
|
||||||
|
operation: string;
|
||||||
|
attempts: number;
|
||||||
|
lastAttempt: string;
|
||||||
|
error: string | null;
|
||||||
|
failed: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FailedItemWithTitle extends FailedItemRow {
|
||||||
|
title?: string;
|
||||||
|
failureType: 'chunks' | 'full';
|
||||||
|
isPermanent: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queues a note for embedding update
|
* Queues a note for embedding update
|
||||||
*/
|
*/
|
||||||
@ -77,17 +92,17 @@ export async function queueNoteForEmbedding(noteId: string, operation = 'UPDATE'
|
|||||||
*/
|
*/
|
||||||
export async function getFailedEmbeddingNotes(limit: number = 100): Promise<any[]> {
|
export async function getFailedEmbeddingNotes(limit: number = 100): Promise<any[]> {
|
||||||
// Get notes with failed embedding attempts or permanently failed flag
|
// Get notes with failed embedding attempts or permanently failed flag
|
||||||
const failedQueueItems = await sql.getRows(`
|
const failedQueueItems = sql.getRows<FailedItemRow>(`
|
||||||
SELECT noteId, operation, attempts, lastAttempt, error, failed
|
SELECT noteId, operation, attempts, lastAttempt, error, failed
|
||||||
FROM embedding_queue
|
FROM embedding_queue
|
||||||
WHERE attempts > 0 OR failed = 1
|
WHERE attempts > 0 OR failed = 1
|
||||||
ORDER BY failed DESC, attempts DESC, lastAttempt DESC
|
ORDER BY failed DESC, attempts DESC, lastAttempt DESC
|
||||||
LIMIT ?`,
|
LIMIT ?`,
|
||||||
[limit]
|
[limit]
|
||||||
) as {noteId: string, operation: string, attempts: number, lastAttempt: string, error: string, failed: number}[];
|
);
|
||||||
|
|
||||||
// Add titles to the failed notes
|
// Add titles to the failed notes
|
||||||
const failedNotesWithTitles = [];
|
const failedNotesWithTitles: FailedItemWithTitle[] = [];
|
||||||
for (const item of failedQueueItems) {
|
for (const item of failedQueueItems) {
|
||||||
const note = becca.getNote(item.noteId);
|
const note = becca.getNote(item.noteId);
|
||||||
if (note) {
|
if (note) {
|
||||||
|
@ -8,6 +8,14 @@ import entityChangesService from "../../../services/entity_changes.js";
|
|||||||
import type { EntityChange } from "../../../services/entity_changes_interface.js";
|
import type { EntityChange } from "../../../services/entity_changes_interface.js";
|
||||||
import { EMBEDDING_CONSTANTS } from "../constants/embedding_constants.js";
|
import { EMBEDDING_CONSTANTS } from "../constants/embedding_constants.js";
|
||||||
import { SEARCH_CONSTANTS } from '../constants/search_constants.js';
|
import { SEARCH_CONSTANTS } from '../constants/search_constants.js';
|
||||||
|
|
||||||
|
interface Similarity {
|
||||||
|
noteId: string;
|
||||||
|
similarity: number;
|
||||||
|
contentType: string;
|
||||||
|
bonuses?: Record<string, number>; // Optional for debugging
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates or updates an embedding for a note
|
* Creates or updates an embedding for a note
|
||||||
*/
|
*/
|
||||||
@ -434,7 +442,7 @@ async function processEmbeddings(queryEmbedding: Float32Array, embeddings: any[]
|
|||||||
return { bonuses, totalBonus };
|
return { bonuses, totalBonus };
|
||||||
}
|
}
|
||||||
|
|
||||||
const similarities = [];
|
const similarities: Similarity[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Try to extract the original query text if it was added to the metadata
|
// Try to extract the original query text if it was added to the metadata
|
||||||
|
@ -95,7 +95,7 @@ export class ModelSelectionStage extends BasePipelineStage<ModelSelectionInput,
|
|||||||
const providerPrecedence = await options.getOption('aiProviderPrecedence');
|
const providerPrecedence = await options.getOption('aiProviderPrecedence');
|
||||||
if (providerPrecedence) {
|
if (providerPrecedence) {
|
||||||
// Parse provider precedence list
|
// Parse provider precedence list
|
||||||
let providers = [];
|
let providers: string[] = [];
|
||||||
if (providerPrecedence.includes(',')) {
|
if (providerPrecedence.includes(',')) {
|
||||||
providers = providerPrecedence.split(',').map(p => p.trim());
|
providers = providerPrecedence.split(',').map(p => p.trim());
|
||||||
} else if (providerPrecedence.startsWith('[') && providerPrecedence.endsWith(']')) {
|
} else if (providerPrecedence.startsWith('[') && providerPrecedence.endsWith(']')) {
|
||||||
|
@ -7,6 +7,21 @@ import { getAnthropicOptions } from './providers.js';
|
|||||||
import log from '../../log.js';
|
import log from '../../log.js';
|
||||||
import Anthropic from '@anthropic-ai/sdk';
|
import Anthropic from '@anthropic-ai/sdk';
|
||||||
import { SEARCH_CONSTANTS } from '../constants/search_constants.js';
|
import { SEARCH_CONSTANTS } from '../constants/search_constants.js';
|
||||||
|
import type { ToolCall } from '../tools/tool_interfaces.js';
|
||||||
|
|
||||||
|
interface AnthropicMessage extends Omit<Message, "content"> {
|
||||||
|
content: MessageContent[] | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MessageContent {
|
||||||
|
type: "text" | "tool_use" | "tool_result";
|
||||||
|
text?: string;
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
content?: string;
|
||||||
|
tool_use_id?: string;
|
||||||
|
input?: string | Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
export class AnthropicService extends BaseAIService {
|
export class AnthropicService extends BaseAIService {
|
||||||
private client: any = null;
|
private client: any = null;
|
||||||
@ -130,7 +145,7 @@ export class AnthropicService extends BaseAIService {
|
|||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
// Process tool calls if any are present in the response
|
// Process tool calls if any are present in the response
|
||||||
let toolCalls = null;
|
let toolCalls: ToolCall[] | null = null;
|
||||||
if (response.content) {
|
if (response.content) {
|
||||||
const toolBlocks = response.content.filter((block: any) =>
|
const toolBlocks = response.content.filter((block: any) =>
|
||||||
block.type === 'tool_use' ||
|
block.type === 'tool_use' ||
|
||||||
@ -157,7 +172,7 @@ export class AnthropicService extends BaseAIService {
|
|||||||
return null;
|
return null;
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
|
|
||||||
log.info(`Extracted ${toolCalls.length} tool calls from Anthropic response`);
|
log.info(`Extracted ${toolCalls?.length} tool calls from Anthropic response`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,8 +421,8 @@ export class AnthropicService extends BaseAIService {
|
|||||||
/**
|
/**
|
||||||
* Format messages for the Anthropic API
|
* Format messages for the Anthropic API
|
||||||
*/
|
*/
|
||||||
private formatMessages(messages: Message[]): any[] {
|
private formatMessages(messages: Message[]): AnthropicMessage[] {
|
||||||
const anthropicMessages: any[] = [];
|
const anthropicMessages: AnthropicMessage[] = [];
|
||||||
|
|
||||||
// Process each message
|
// Process each message
|
||||||
for (const msg of messages) {
|
for (const msg of messages) {
|
||||||
@ -424,7 +439,7 @@ export class AnthropicService extends BaseAIService {
|
|||||||
// Assistant messages need special handling for tool_calls
|
// Assistant messages need special handling for tool_calls
|
||||||
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
||||||
// Create content blocks array for tool calls
|
// Create content blocks array for tool calls
|
||||||
const content = [];
|
const content: MessageContent[] = [];
|
||||||
|
|
||||||
// Add text content if present
|
// Add text content if present
|
||||||
if (msg.content) {
|
if (msg.content) {
|
||||||
|
@ -181,7 +181,7 @@ export async function updateEmbeddingProviderConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build update query parts
|
// Build update query parts
|
||||||
const updates = [];
|
const updates: string[] = [];
|
||||||
const params: any[] = [];
|
const params: any[] = [];
|
||||||
|
|
||||||
if (priority !== undefined) {
|
if (priority !== undefined) {
|
||||||
|
@ -8,6 +8,26 @@ import type { Tool, ToolHandler } from './tool_interfaces.js';
|
|||||||
import log from '../../log.js';
|
import log from '../../log.js';
|
||||||
import becca from '../../../becca/becca.js';
|
import becca from '../../../becca/becca.js';
|
||||||
|
|
||||||
|
interface CodeBlock {
|
||||||
|
code: string;
|
||||||
|
language?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Heading {
|
||||||
|
text: string;
|
||||||
|
level: number; // 1 for H1, 2 for H2, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
interface List {
|
||||||
|
type: "unordered" | "ordered";
|
||||||
|
items: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Table {
|
||||||
|
headers: string[];
|
||||||
|
rows: string[][];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of the content extraction tool
|
* Definition of the content extraction tool
|
||||||
*/
|
*/
|
||||||
@ -137,8 +157,8 @@ export class ContentExtractionTool implements ToolHandler {
|
|||||||
/**
|
/**
|
||||||
* Extract lists from HTML content
|
* Extract lists from HTML content
|
||||||
*/
|
*/
|
||||||
private extractLists(content: string): Array<{ type: string, items: string[] }> {
|
private extractLists(content: string): List[] {
|
||||||
const lists = [];
|
const lists: List[] = [];
|
||||||
|
|
||||||
// Extract unordered lists
|
// Extract unordered lists
|
||||||
const ulRegex = /<ul[^>]*>([\s\S]*?)<\/ul>/gi;
|
const ulRegex = /<ul[^>]*>([\s\S]*?)<\/ul>/gi;
|
||||||
@ -179,7 +199,7 @@ export class ContentExtractionTool implements ToolHandler {
|
|||||||
* Extract list items from list content
|
* Extract list items from list content
|
||||||
*/
|
*/
|
||||||
private extractListItems(listContent: string): string[] {
|
private extractListItems(listContent: string): string[] {
|
||||||
const items = [];
|
const items: string[] = [];
|
||||||
const itemRegex = /<li[^>]*>([\s\S]*?)<\/li>/gi;
|
const itemRegex = /<li[^>]*>([\s\S]*?)<\/li>/gi;
|
||||||
let itemMatch;
|
let itemMatch;
|
||||||
|
|
||||||
@ -196,15 +216,15 @@ export class ContentExtractionTool implements ToolHandler {
|
|||||||
/**
|
/**
|
||||||
* Extract tables from HTML content
|
* Extract tables from HTML content
|
||||||
*/
|
*/
|
||||||
private extractTables(content: string): Array<{ headers: string[], rows: string[][] }> {
|
private extractTables(content: string): Table[] {
|
||||||
const tables = [];
|
const tables: Table[] = [];
|
||||||
const tableRegex = /<table[^>]*>([\s\S]*?)<\/table>/gi;
|
const tableRegex = /<table[^>]*>([\s\S]*?)<\/table>/gi;
|
||||||
let tableMatch;
|
let tableMatch: RegExpExecArray | null;
|
||||||
|
|
||||||
while ((tableMatch = tableRegex.exec(content)) !== null) {
|
while ((tableMatch = tableRegex.exec(content)) !== null) {
|
||||||
const tableContent = tableMatch[1];
|
const tableContent = tableMatch[1];
|
||||||
const headers = [];
|
const headers: string[] = [];
|
||||||
const rows = [];
|
const rows: string[][] = [];
|
||||||
|
|
||||||
// Extract table headers
|
// Extract table headers
|
||||||
const headerRegex = /<th[^>]*>([\s\S]*?)<\/th>/gi;
|
const headerRegex = /<th[^>]*>([\s\S]*?)<\/th>/gi;
|
||||||
@ -218,7 +238,7 @@ export class ContentExtractionTool implements ToolHandler {
|
|||||||
let rowMatch;
|
let rowMatch;
|
||||||
while ((rowMatch = rowRegex.exec(tableContent)) !== null) {
|
while ((rowMatch = rowRegex.exec(tableContent)) !== null) {
|
||||||
const rowContent = rowMatch[1];
|
const rowContent = rowMatch[1];
|
||||||
const cells = [];
|
const cells: string[] = [];
|
||||||
|
|
||||||
const cellRegex = /<td[^>]*>([\s\S]*?)<\/td>/gi;
|
const cellRegex = /<td[^>]*>([\s\S]*?)<\/td>/gi;
|
||||||
let cellMatch;
|
let cellMatch;
|
||||||
@ -246,7 +266,7 @@ export class ContentExtractionTool implements ToolHandler {
|
|||||||
* Extract headings from HTML content
|
* Extract headings from HTML content
|
||||||
*/
|
*/
|
||||||
private extractHeadings(content: string): Array<{ level: number, text: string }> {
|
private extractHeadings(content: string): Array<{ level: number, text: string }> {
|
||||||
const headings = [];
|
const headings: Heading[] = [];
|
||||||
|
|
||||||
for (let i = 1; i <= 6; i++) {
|
for (let i = 1; i <= 6; i++) {
|
||||||
const headingRegex = new RegExp(`<h${i}[^>]*>([\\s\\S]*?)<\/h${i}>`, 'gi');
|
const headingRegex = new RegExp(`<h${i}[^>]*>([\\s\\S]*?)<\/h${i}>`, 'gi');
|
||||||
@ -270,7 +290,7 @@ export class ContentExtractionTool implements ToolHandler {
|
|||||||
* Extract code blocks from HTML content
|
* Extract code blocks from HTML content
|
||||||
*/
|
*/
|
||||||
private extractCodeBlocks(content: string): Array<{ language?: string, code: string }> {
|
private extractCodeBlocks(content: string): Array<{ language?: string, code: string }> {
|
||||||
const codeBlocks = [];
|
const codeBlocks: CodeBlock[] = [];
|
||||||
|
|
||||||
// Look for <pre> and <code> blocks
|
// Look for <pre> and <code> blocks
|
||||||
const preRegex = /<pre[^>]*>([\s\S]*?)<\/pre>/gi;
|
const preRegex = /<pre[^>]*>([\s\S]*?)<\/pre>/gi;
|
||||||
|
@ -9,6 +9,7 @@ import log from '../../log.js';
|
|||||||
import becca from '../../../becca/becca.js';
|
import becca from '../../../becca/becca.js';
|
||||||
import notes from '../../notes.js';
|
import notes from '../../notes.js';
|
||||||
import attributes from '../../attributes.js';
|
import attributes from '../../attributes.js';
|
||||||
|
import type { BNote } from '../../backend_script_entrypoint.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of the note creation tool
|
* Definition of the note creation tool
|
||||||
@ -89,7 +90,7 @@ export class NoteCreationTool implements ToolHandler {
|
|||||||
log.info(`Executing create_note tool - Title: "${title}", Type: ${type}, ParentNoteId: ${parentNoteId || 'root'}`);
|
log.info(`Executing create_note tool - Title: "${title}", Type: ${type}, ParentNoteId: ${parentNoteId || 'root'}`);
|
||||||
|
|
||||||
// Validate parent note exists if specified
|
// Validate parent note exists if specified
|
||||||
let parent = null;
|
let parent: BNote | null = null;
|
||||||
if (parentNoteId) {
|
if (parentNoteId) {
|
||||||
parent = becca.notes[parentNoteId];
|
parent = becca.notes[parentNoteId];
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
|
@ -10,6 +10,14 @@ import becca from '../../../becca/becca.js';
|
|||||||
import attributes from '../../attributes.js';
|
import attributes from '../../attributes.js';
|
||||||
import aiServiceManager from '../ai_service_manager.js';
|
import aiServiceManager from '../ai_service_manager.js';
|
||||||
import { SEARCH_CONSTANTS } from '../constants/search_constants.js';
|
import { SEARCH_CONSTANTS } from '../constants/search_constants.js';
|
||||||
|
import type { Backlink, RelatedNote } from '../embeddings/embeddings_interface.js';
|
||||||
|
|
||||||
|
interface Suggestion {
|
||||||
|
targetNoteId: string;
|
||||||
|
targetTitle: string;
|
||||||
|
similarity: number;
|
||||||
|
suggestedRelation: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of the relationship tool
|
* Definition of the relationship tool
|
||||||
@ -180,7 +188,7 @@ export class RelationshipTool implements ToolHandler {
|
|||||||
.filter((attr: any) => attr.type === 'relation')
|
.filter((attr: any) => attr.type === 'relation')
|
||||||
.slice(0, limit);
|
.slice(0, limit);
|
||||||
|
|
||||||
const outgoingRelations = [];
|
const outgoingRelations: RelatedNote[] = [];
|
||||||
|
|
||||||
for (const attr of outgoingAttributes) {
|
for (const attr of outgoingAttributes) {
|
||||||
const targetNote = becca.notes[attr.value];
|
const targetNote = becca.notes[attr.value];
|
||||||
@ -196,7 +204,7 @@ export class RelationshipTool implements ToolHandler {
|
|||||||
|
|
||||||
// Get incoming relationships (where this note is the target)
|
// Get incoming relationships (where this note is the target)
|
||||||
// Since becca.findNotesWithRelation doesn't exist, use attributes to find notes with relation
|
// Since becca.findNotesWithRelation doesn't exist, use attributes to find notes with relation
|
||||||
const incomingRelations = [];
|
const incomingRelations: Backlink[] = [];
|
||||||
|
|
||||||
// Find all attributes of type relation that point to this note
|
// Find all attributes of type relation that point to this note
|
||||||
const relationAttributes = sourceNote.getTargetRelations();
|
const relationAttributes = sourceNote.getTargetRelations();
|
||||||
@ -321,7 +329,7 @@ export class RelationshipTool implements ToolHandler {
|
|||||||
const sourceContent = await sourceNote.getContent();
|
const sourceContent = await sourceNote.getContent();
|
||||||
|
|
||||||
// Prepare suggestions
|
// Prepare suggestions
|
||||||
const suggestions = [];
|
const suggestions: Suggestion[] = [];
|
||||||
|
|
||||||
for (const relatedNote of relatedResult.relatedNotes) {
|
for (const relatedNote of relatedResult.relatedNotes) {
|
||||||
try {
|
try {
|
||||||
|
@ -755,7 +755,7 @@ function updateNoteData(noteId: string, content: string, attachments: Attachment
|
|||||||
function undeleteNote(noteId: string, taskContext: TaskContext) {
|
function undeleteNote(noteId: string, taskContext: TaskContext) {
|
||||||
const noteRow = sql.getRow<NoteRow>("SELECT * FROM notes WHERE noteId = ?", [noteId]);
|
const noteRow = sql.getRow<NoteRow>("SELECT * FROM notes WHERE noteId = ?", [noteId]);
|
||||||
|
|
||||||
if (!noteRow.isDeleted) {
|
if (!noteRow.isDeleted || !noteRow.deleteId) {
|
||||||
log.error(`Note '${noteId}' is not deleted and thus cannot be undeleted.`);
|
log.error(`Note '${noteId}' is not deleted and thus cannot be undeleted.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ function getParams(params?: ScriptParams) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getScriptBundleForFrontend(note: BNote, script?: string, params?: ScriptParams) {
|
function getScriptBundleForFrontend(note: BNote, script?: string, params?: ScriptParams) {
|
||||||
let overrideContent = null;
|
let overrideContent: string | null = null;
|
||||||
|
|
||||||
if (script) {
|
if (script) {
|
||||||
overrideContent = `return (${script}\r\n)(${getParams(params)})`;
|
overrideContent = `return (${script}\r\n)(${getParams(params)})`;
|
||||||
@ -170,7 +170,7 @@ function getScriptBundle(note: BNote, root: boolean = true, scriptEnv: string |
|
|||||||
|
|
||||||
includedNoteIds.push(note.noteId);
|
includedNoteIds.push(note.noteId);
|
||||||
|
|
||||||
const modules = [];
|
const modules: BNote[] = [];
|
||||||
|
|
||||||
for (const child of note.getChildNotes()) {
|
for (const child of note.getChildNotes()) {
|
||||||
const childBundle = getScriptBundle(child, false, scriptEnv, includedNoteIds);
|
const childBundle = getScriptBundle(child, false, scriptEnv, includedNoteIds);
|
||||||
|
@ -52,7 +52,7 @@ class NoteFlatTextExp extends Expression {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundAttrTokens = [];
|
const foundAttrTokens: string[] = [];
|
||||||
|
|
||||||
for (const token of remainingTokens) {
|
for (const token of remainingTokens) {
|
||||||
if (note.type.includes(token) || note.mime.includes(token)) {
|
if (note.type.includes(token) || note.mime.includes(token)) {
|
||||||
@ -73,7 +73,7 @@ class NoteFlatTextExp extends Expression {
|
|||||||
|
|
||||||
for (const parentNote of note.parents) {
|
for (const parentNote of note.parents) {
|
||||||
const title = normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId));
|
const title = normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId));
|
||||||
const foundTokens = foundAttrTokens.slice();
|
const foundTokens: string[] = foundAttrTokens.slice();
|
||||||
|
|
||||||
for (const token of remainingTokens) {
|
for (const token of remainingTokens) {
|
||||||
if (title.includes(token)) {
|
if (title.includes(token)) {
|
||||||
@ -100,7 +100,7 @@ class NoteFlatTextExp extends Expression {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundAttrTokens = [];
|
const foundAttrTokens: string[] = [];
|
||||||
|
|
||||||
for (const token of this.tokens) {
|
for (const token of this.tokens) {
|
||||||
if (note.type.includes(token) || note.mime.includes(token)) {
|
if (note.type.includes(token) || note.mime.includes(token)) {
|
||||||
@ -153,7 +153,7 @@ class NoteFlatTextExp extends Expression {
|
|||||||
* Returns noteIds which have at least one matching tokens
|
* Returns noteIds which have at least one matching tokens
|
||||||
*/
|
*/
|
||||||
getCandidateNotes(noteSet: NoteSet): BNote[] {
|
getCandidateNotes(noteSet: NoteSet): BNote[] {
|
||||||
const candidateNotes = [];
|
const candidateNotes: BNote[] = [];
|
||||||
|
|
||||||
for (const note of noteSet.notes) {
|
for (const note of noteSet.notes) {
|
||||||
for (const token of this.tokens) {
|
for (const token of this.tokens) {
|
||||||
|
@ -294,11 +294,11 @@ function getExpression(tokens: TokenData[], searchContext: SearchContext, level
|
|||||||
valueExtractor: ValueExtractor;
|
valueExtractor: ValueExtractor;
|
||||||
direction: string;
|
direction: string;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
let limit;
|
let limit: number | undefined = undefined;
|
||||||
|
|
||||||
if (tokens[i].token === "orderby") {
|
if (tokens[i].token === "orderby") {
|
||||||
do {
|
do {
|
||||||
const propertyPath = [];
|
const propertyPath: string[] = [];
|
||||||
let direction = "asc";
|
let direction = "asc";
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -36,7 +36,7 @@ function searchFromNote(note: BNote): SearchNoteResult {
|
|||||||
|
|
||||||
const searchScript = note.getRelationValue("searchScript");
|
const searchScript = note.getRelationValue("searchScript");
|
||||||
const searchString = note.getLabelValue("searchString") || "";
|
const searchString = note.getLabelValue("searchString") || "";
|
||||||
let error = null;
|
let error: string | null = null;
|
||||||
|
|
||||||
if (searchScript) {
|
if (searchScript) {
|
||||||
searchResultNoteIds = searchFromRelation(note, "searchScript");
|
searchResultNoteIds = searchFromRelation(note, "searchScript");
|
||||||
|
@ -43,7 +43,7 @@ export default class Shaca {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getNotes(noteIds: string[], ignoreMissing = false) {
|
getNotes(noteIds: string[], ignoreMissing = false) {
|
||||||
const filteredNotes = [];
|
const filteredNotes: SNote[] = [];
|
||||||
|
|
||||||
for (const noteId of noteIds) {
|
for (const noteId of noteIds) {
|
||||||
const note = this.notes[noteId];
|
const note = this.notes[noteId];
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"strict": false,
|
|
||||||
"types": [
|
"types": [
|
||||||
"node",
|
"node",
|
||||||
"express"
|
"express"
|
||||||
@ -14,7 +13,8 @@
|
|||||||
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo"
|
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts"
|
"src/**/*.ts",
|
||||||
|
"package.json"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"eslint.config.js",
|
"eslint.config.js",
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"target": "ES2020",
|
||||||
"outDir": "./out-tsc/vitest",
|
"outDir": "./out-tsc/vitest",
|
||||||
"types": [
|
"types": [
|
||||||
"vitest/globals",
|
"vitest/globals",
|
||||||
@ -24,6 +27,7 @@
|
|||||||
"src/**/*.test.jsx",
|
"src/**/*.test.jsx",
|
||||||
"src/**/*.spec.jsx",
|
"src/**/*.spec.jsx",
|
||||||
"src/**/*.d.ts",
|
"src/**/*.d.ts",
|
||||||
"src/**/*.ts"
|
"src/**/*.ts",
|
||||||
|
"package.json"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
10
nx.json
10
nx.json
@ -62,16 +62,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"targetDefaults": {
|
"targetDefaults": {
|
||||||
"@nx/js:swc": {
|
|
||||||
"cache": true,
|
|
||||||
"dependsOn": [
|
|
||||||
"^build"
|
|
||||||
],
|
|
||||||
"inputs": [
|
|
||||||
"production",
|
|
||||||
"^production"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"test": {
|
"test": {
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"^build"
|
"^build"
|
||||||
|
@ -38,10 +38,6 @@
|
|||||||
"@nx/vite": "21.1.2",
|
"@nx/vite": "21.1.2",
|
||||||
"@nx/web": "21.1.2",
|
"@nx/web": "21.1.2",
|
||||||
"@playwright/test": "^1.36.0",
|
"@playwright/test": "^1.36.0",
|
||||||
"@swc-node/register": "~1.10.0",
|
|
||||||
"@swc/cli": "~0.7.0",
|
|
||||||
"@swc/core": "~1.11.0",
|
|
||||||
"@swc/helpers": "~0.5.11",
|
|
||||||
"@triliumnext/server": "workspace:*",
|
"@triliumnext/server": "workspace:*",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"@types/node": "22.15.24",
|
"@types/node": "22.15.24",
|
||||||
@ -59,7 +55,6 @@
|
|||||||
"jsonc-eslint-parser": "^2.1.0",
|
"jsonc-eslint-parser": "^2.1.0",
|
||||||
"nx": "21.1.2",
|
"nx": "21.1.2",
|
||||||
"react-refresh": "^0.17.0",
|
"react-refresh": "^0.17.0",
|
||||||
"swc-loader": "0.2.6",
|
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"tsx": "4.19.4",
|
"tsx": "4.19.4",
|
||||||
"typescript": "~5.8.0",
|
"typescript": "~5.8.0",
|
||||||
|
@ -22,28 +22,3 @@ declare global {
|
|||||||
importMarkdownInline(): void;
|
importMarkdownInline(): void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module "ckeditor5" {
|
|
||||||
interface Editor {
|
|
||||||
getSelectedHtml(): string;
|
|
||||||
removeSelection(): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EditorConfig {
|
|
||||||
syntaxHighlighting?: {
|
|
||||||
loadHighlightJs: () => Promise<any>;
|
|
||||||
mapLanguageName(mimeType: string): string;
|
|
||||||
defaultMimeType: string;
|
|
||||||
enabled: boolean;
|
|
||||||
},
|
|
||||||
moveBlockUp?: {
|
|
||||||
keystroke: string[];
|
|
||||||
},
|
|
||||||
moveBlockDown?: {
|
|
||||||
keystroke: string[];
|
|
||||||
},
|
|
||||||
clipboard?: {
|
|
||||||
copy(text: string): void;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -5,6 +5,10 @@ import { BalloonEditor, DecoupledEditor, FindAndReplaceEditing, FindCommand } fr
|
|||||||
export { EditorWatchdog } from "ckeditor5";
|
export { EditorWatchdog } from "ckeditor5";
|
||||||
export type { EditorConfig, MentionFeed, MentionFeedObjectItem, Node, Position, Element, WatchdogConfig } from "ckeditor5";
|
export type { EditorConfig, MentionFeed, MentionFeedObjectItem, Node, Position, Element, WatchdogConfig } from "ckeditor5";
|
||||||
|
|
||||||
|
// Import with sideffects to ensure that type augmentations are present.
|
||||||
|
import "@triliumnext/ckeditor5-math";
|
||||||
|
import "@triliumnext/ckeditor5-mermaid";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Short-hand for the CKEditor classes supported by Trilium for text editing.
|
* Short-hand for the CKEditor classes supported by Trilium for text editing.
|
||||||
* Specialized editors such as the {@link AttributeEditor} are not included.
|
* Specialized editors such as the {@link AttributeEditor} are not included.
|
||||||
@ -43,3 +47,28 @@ export class PopupEditor extends BalloonEditor {
|
|||||||
return POPUP_EDITOR_PLUGINS;
|
return POPUP_EDITOR_PLUGINS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "ckeditor5" {
|
||||||
|
interface Editor {
|
||||||
|
getSelectedHtml(): string;
|
||||||
|
removeSelection(): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditorConfig {
|
||||||
|
syntaxHighlighting?: {
|
||||||
|
loadHighlightJs: () => Promise<any>;
|
||||||
|
mapLanguageName(mimeType: string): string;
|
||||||
|
defaultMimeType: string;
|
||||||
|
enabled: boolean;
|
||||||
|
},
|
||||||
|
moveBlockUp?: {
|
||||||
|
keystroke: string[];
|
||||||
|
},
|
||||||
|
moveBlockDown?: {
|
||||||
|
keystroke: string[];
|
||||||
|
},
|
||||||
|
clipboard?: {
|
||||||
|
copy(text: string): void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import type { Element, Writer } from "ckeditor5";
|
import type { Element, Position, Writer } from "ckeditor5";
|
||||||
import type { Node, Editor } from "ckeditor5";
|
import type { Node, Editor } from "ckeditor5";
|
||||||
import { Plugin } from "ckeditor5";
|
import { Plugin } from "ckeditor5";
|
||||||
|
|
||||||
|
interface SpanStackEntry {
|
||||||
|
className: string;
|
||||||
|
posStart: Position;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This code is an adaptation of https://github.com/antoniotejada/Trilium-SyntaxHighlightWidget with additional improvements, such as:
|
* This code is an adaptation of https://github.com/antoniotejada/Trilium-SyntaxHighlightWidget with additional improvements, such as:
|
||||||
*
|
*
|
||||||
@ -234,10 +239,10 @@ export default class SyntaxHighlighting extends Plugin {
|
|||||||
|
|
||||||
let iHtml = 0;
|
let iHtml = 0;
|
||||||
let html = highlightRes.value;
|
let html = highlightRes.value;
|
||||||
let spanStack = [];
|
let spanStack: SpanStackEntry[] = [];
|
||||||
let iChild = -1;
|
let iChild = -1;
|
||||||
let childText = "";
|
let childText = "";
|
||||||
let child = null;
|
let child: Node | null = null;
|
||||||
let iChildText = 0;
|
let iChildText = 0;
|
||||||
|
|
||||||
while (iHtml < html.length) {
|
while (iHtml < html.length) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { indentLess, indentMore } from "@codemirror/commands";
|
import { indentLess, indentMore } from "@codemirror/commands";
|
||||||
import { EditorSelection, EditorState, type ChangeSpec } from "@codemirror/state";
|
import { EditorSelection, EditorState, SelectionRange, type ChangeSpec } from "@codemirror/state";
|
||||||
import type { KeyBinding } from "@codemirror/view";
|
import type { KeyBinding } from "@codemirror/view";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,8 +19,8 @@ const smartIndentWithTab: KeyBinding[] = [
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { selection } = state;
|
const { selection } = state;
|
||||||
const changes = [];
|
const changes: ChangeSpec[] = [];
|
||||||
const newSelections = [];
|
const newSelections: SelectionRange[] = [];
|
||||||
|
|
||||||
// Step 1: Handle non-empty selections → replace with tab
|
// Step 1: Handle non-empty selections → replace with tab
|
||||||
if (selection.ranges.some(range => !range.empty)) {
|
if (selection.ranges.some(range => !range.empty)) {
|
||||||
|
@ -23,6 +23,6 @@
|
|||||||
"src/**/*.spec.js",
|
"src/**/*.spec.js",
|
||||||
"src/**/*.test.jsx",
|
"src/**/*.test.jsx",
|
||||||
"src/**/*.spec.jsx",
|
"src/**/*.spec.jsx",
|
||||||
"src/**/*.d.ts"
|
"src/**/*.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"jsc": {
|
|
||||||
"target": "es2017",
|
|
||||||
"parser": {
|
|
||||||
"syntax": "typescript",
|
|
||||||
"decorators": true,
|
|
||||||
"dynamicImport": true
|
|
||||||
},
|
|
||||||
"transform": {
|
|
||||||
"decoratorMetadata": true,
|
|
||||||
"legacyDecorator": true
|
|
||||||
},
|
|
||||||
"keepClassNames": true,
|
|
||||||
"externalHelpers": true,
|
|
||||||
"loose": true
|
|
||||||
},
|
|
||||||
"module": {
|
|
||||||
"type": "es6"
|
|
||||||
},
|
|
||||||
"sourceMaps": true,
|
|
||||||
"exclude": ["jest.config.ts",".*\\.spec.tsx?$",".*\\.test.tsx?$","./src/jest-setup.ts$","./**/jest-setup.ts$"]
|
|
||||||
}
|
|
@ -4,16 +4,16 @@
|
|||||||
"description": "Shared library between the clients (e.g. browser, Electron) and the server, mostly for type definitions and utility methods.",
|
"description": "Shared library between the clients (e.g. browser, Electron) and the server, mostly for type definitions and utility methods.",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/main.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/main.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
"./package.json": "./package.json",
|
"./package.json": "./package.json",
|
||||||
".": {
|
".": {
|
||||||
"development": "./src/index.ts",
|
"development": "./src/index.ts",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"import": "./dist/index.js",
|
"import": "./dist/main.js",
|
||||||
"default": "./dist/index.js"
|
"default": "./dist/main.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
@ -27,21 +27,31 @@
|
|||||||
"sourceRoot": "packages/commons/src",
|
"sourceRoot": "packages/commons/src",
|
||||||
"targets": {
|
"targets": {
|
||||||
"build": {
|
"build": {
|
||||||
"executor": "@nx/js:swc",
|
"executor": "@nx/esbuild:esbuild",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"{options.outputPath}"
|
"{options.outputPath}"
|
||||||
],
|
],
|
||||||
|
"defaultConfiguration": "production",
|
||||||
"options": {
|
"options": {
|
||||||
"outputPath": "packages/commons/dist",
|
|
||||||
"main": "packages/commons/src/index.ts",
|
"main": "packages/commons/src/index.ts",
|
||||||
|
"outputPath": "packages/commons/dist",
|
||||||
|
"outputFileName": "main.js",
|
||||||
"tsConfig": "packages/commons/tsconfig.lib.json",
|
"tsConfig": "packages/commons/tsconfig.lib.json",
|
||||||
"skipTypeCheck": true,
|
"platform": "node",
|
||||||
"stripLeadingPaths": true
|
"format": [
|
||||||
|
"esm"
|
||||||
|
],
|
||||||
|
"declarationRootDir": "packages/commons/src"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"development": {
|
||||||
|
"minify": false
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"minify": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@swc/helpers": "~0.5.11"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,17 +126,17 @@ export type NoteType = (typeof ALLOWED_NOTE_TYPES)[number];
|
|||||||
|
|
||||||
export interface NoteRow {
|
export interface NoteRow {
|
||||||
noteId: string;
|
noteId: string;
|
||||||
deleteId: string;
|
deleteId?: string;
|
||||||
title: string;
|
title: string;
|
||||||
type: NoteType;
|
type: NoteType;
|
||||||
mime: string;
|
mime: string;
|
||||||
isProtected: boolean;
|
isProtected?: boolean;
|
||||||
isDeleted: boolean;
|
isDeleted?: boolean;
|
||||||
blobId: string;
|
blobId?: string;
|
||||||
dateCreated: string;
|
dateCreated?: string;
|
||||||
dateModified: string;
|
dateModified?: string;
|
||||||
utcDateCreated: string;
|
utcDateCreated?: string;
|
||||||
utcDateModified: string;
|
utcDateModified?: string;
|
||||||
content?: string | Buffer;
|
content?: string | Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ export function trimIndentation(strings: TemplateStringsArray, ...values: any[])
|
|||||||
|
|
||||||
// Trim the indentation of the first line in all the lines.
|
// Trim the indentation of the first line in all the lines.
|
||||||
const lines = str.split("\n");
|
const lines = str.split("\n");
|
||||||
const output = [];
|
const output: string[] = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
let numSpacesLine = 0;
|
let numSpacesLine = 0;
|
||||||
while (str.charAt(numSpacesLine) == " " && numSpacesLine < str.length) {
|
while (str.charAt(numSpacesLine) == " " && numSpacesLine < str.length) {
|
||||||
|
@ -56,9 +56,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
|
||||||
"@swc/helpers": "~0.5.11"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"turndown": "7.2.0",
|
"turndown": "7.2.0",
|
||||||
"turndown-attendant": "0.0.3"
|
"turndown-attendant": "0.0.3"
|
||||||
|
637
pnpm-lock.yaml
generated
637
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,6 @@ ignoredBuiltDependencies:
|
|||||||
onlyBuiltDependencies:
|
onlyBuiltDependencies:
|
||||||
- '@parcel/watcher'
|
- '@parcel/watcher'
|
||||||
- '@scarf/scarf'
|
- '@scarf/scarf'
|
||||||
- '@swc/core'
|
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- core-js-pure
|
- core-js-pure
|
||||||
- esbuild
|
- esbuild
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"target": "es2022",
|
"target": "es2022",
|
||||||
"customConditions": ["development"],
|
"customConditions": ["development"],
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": false, // TODO: Re-enable it when migrating back to ESM.
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user