chore(prettier): fix code style

This commit is contained in:
Panagiotis Papadopoulos 2025-03-02 20:47:57 +01:00
parent 67509bc92f
commit 2beaaa95bf
112 changed files with 857 additions and 819 deletions

View File

@ -1,6 +1,6 @@
import { fileURLToPath } from "url";
import { dirname, join } from "path";
import swaggerJsdoc from 'swagger-jsdoc';
import swaggerJsdoc from "swagger-jsdoc";
import fs from "fs";
/*
@ -11,28 +11,30 @@ import fs from "fs";
*/
const options = {
definition: {
openapi: '3.1.1',
info: {
title: 'Trilium Notes - Sync server API',
version: '0.96.6',
description: "This is the internal sync server API used by Trilium Notes / TriliumNext Notes.\n\n_If you're looking for the officially supported External Trilium API, see [here](https://triliumnext.github.io/Docs/Wiki/etapi.html)._\n\nThis page does not yet list all routes. For a full list, see the [route controller](https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/routes.ts).",
contact: {
name: "TriliumNext issue tracker",
url: "https://github.com/TriliumNext/Notes/issues",
},
license: {
name: "GNU Free Documentation License 1.3 (or later)",
url: "https://www.gnu.org/licenses/fdl-1.3",
},
definition: {
openapi: "3.1.1",
info: {
title: "Trilium Notes - Sync server API",
version: "0.96.6",
description:
"This is the internal sync server API used by Trilium Notes / TriliumNext Notes.\n\n_If you're looking for the officially supported External Trilium API, see [here](https://triliumnext.github.io/Docs/Wiki/etapi.html)._\n\nThis page does not yet list all routes. For a full list, see the [route controller](https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/routes.ts).",
contact: {
name: "TriliumNext issue tracker",
url: "https://github.com/TriliumNext/Notes/issues"
},
license: {
name: "GNU Free Documentation License 1.3 (or later)",
url: "https://www.gnu.org/licenses/fdl-1.3"
}
}
},
},
apis: [
// Put individual files here to have them ordered first.
'./src/routes/api/setup.ts',
// all other files
'./src/routes/api/*.ts', './bin/generate-openapi.js'
],
apis: [
// Put individual files here to have them ordered first.
"./src/routes/api/setup.ts",
// all other files
"./src/routes/api/*.ts",
"./bin/generate-openapi.js"
]
};
const openapiSpecification = swaggerJsdoc(options);

View File

@ -11,16 +11,14 @@ test("Displays translation on desktop", async ({ page, context }) => {
const app = new App(page, context);
await app.goto();
await expect(page.locator("#left-pane .quick-search input"))
.toHaveAttribute("placeholder", "Quick search");
await expect(page.locator("#left-pane .quick-search input")).toHaveAttribute("placeholder", "Quick search");
});
test("Displays translation on mobile", async ({ page, context }) => {
const app = new App(page, context);
await app.goto({ isMobile: true });
await expect(page.locator("#mobile-sidebar-wrapper .quick-search input"))
.toHaveAttribute("placeholder", "Quick search");
await expect(page.locator("#mobile-sidebar-wrapper .quick-search input")).toHaveAttribute("placeholder", "Quick search");
});
test("Displays translations in Settings", async ({ page, context }) => {

View File

@ -19,13 +19,13 @@ test("Can drag tabs around", async ({ page, context }) => {
let tab = app.getTab(0);
// Drag the first tab at the end
await tab.dragTo(app.getTab(2), { targetPosition: { x: 50, y: 0 }});
await tab.dragTo(app.getTab(2), { targetPosition: { x: 50, y: 0 } });
tab = app.getTab(2);
await expect(tab).toContainText(NOTE_TITLE);
// Drag the tab to the left
await tab.dragTo(app.getTab(0), { targetPosition: { x: 50, y: 0 }});
await tab.dragTo(app.getTab(0), { targetPosition: { x: 50, y: 0 } });
await expect(app.getTab(0)).toContainText(NOTE_TITLE);
});

View File

@ -39,7 +39,9 @@ test("Displays lint errors for backend script", async ({ page, context }) => {
});
async function expectTooltip(page: Page, tooltip: string) {
await expect(page.locator(".CodeMirror-lint-tooltip:visible", {
"hasText": tooltip
})).toBeVisible();
await expect(
page.locator(".CodeMirror-lint-tooltip:visible", {
hasText: tooltip
})
).toBeVisible();
}

View File

@ -3,7 +3,8 @@ import App from "../support/app";
test("renders ELK flowchart", async ({ page, context }) => {
await testAriaSnapshot({
page, context,
page,
context,
noteTitle: "Flowchart ELK on",
snapshot: `
- document:
@ -22,12 +23,13 @@ test("renders ELK flowchart", async ({ page, context }) => {
- paragraph: Guarantee
- text: Interfaces for B
`
})
});
});
test("renders standard flowchart", async ({ page, context }) => {
await testAriaSnapshot({
page, context,
page,
context,
noteTitle: "Flowchart ELK off",
snapshot: `
- document:
@ -46,7 +48,7 @@ test("renders standard flowchart", async ({ page, context }) => {
- paragraph: C
- text: Interfaces for B
`
})
});
});
interface AriaTestOpts {

View File

@ -44,8 +44,8 @@ test("Highlights list is displayed", async ({ page, context }) => {
await expect(app.sidebar).toContainText("Highlights List");
const rootList = app.sidebar.locator(".highlights-list ol");
let index=0;
for (const highlightedEl of [ "Bold 1", "Italic 1", "Underline 1", "Colored text 1", "Background text 1", "Bold 2", "Italic 2", "Underline 2", "Colored text 2", "Background text 2" ]) {
let index = 0;
for (const highlightedEl of ["Bold 1", "Italic 1", "Underline 1", "Colored text 1", "Background text 1", "Bold 2", "Italic 2", "Underline 2", "Colored text 2", "Background text 2"]) {
await expect(rootList.locator("li").nth(index++)).toContainText(highlightedEl);
}
});
@ -54,7 +54,7 @@ test("Displays math popup", async ({ page, context }) => {
const app = new App(page, context);
await app.goto();
await app.goToNoteInNewTab("Empty text");
const noteContent = app.currentNoteSplit.locator(".note-detail-editable-text-editor")
const noteContent = app.currentNoteSplit.locator(".note-detail-editable-text-editor");
await noteContent.fill("Hello world");
await noteContent.press("ControlOrMeta+M");

View File

@ -25,7 +25,7 @@ export default class App {
this.tabBar = page.locator(".tab-row-widget-container");
this.noteTree = page.locator(".tree-wrapper");
this.launcherBar = page.locator("#launcher-container");
this.currentNoteSplit = page.locator(".note-split:not(.hidden-ext)")
this.currentNoteSplit = page.locator(".note-split:not(.hidden-ext)");
this.sidebar = page.locator("#right-pane");
}
@ -46,8 +46,7 @@ export default class App {
// Wait for the page to load.
if (url === "/") {
await expect(this.page.locator(".tree"))
.toContainText("Trilium Integration Test");
await expect(this.page.locator(".tree")).toContainText("Trilium Integration Test");
await this.closeAllTabs();
}
}
@ -109,11 +108,12 @@ export default class App {
});
expect(csrfToken).toBeTruthy();
await expect(await this.page.request.put(`${BASE_URL}/api/options/${key}/${value}`, {
headers: {
"x-csrf-token": csrfToken
}
})).toBeOK();
await expect(
await this.page.request.put(`${BASE_URL}/api/options/${key}/${value}`, {
headers: {
"x-csrf-token": csrfToken
}
})
).toBeOK();
}
}

View File

@ -1,6 +1,6 @@
import { defineConfig, devices } from '@playwright/test';
import { defineConfig, devices } from "@playwright/test";
const SERVER_URL = 'http://127.0.0.1:8082';
const SERVER_URL = "http://127.0.0.1:8082";
/**
* Read environment variables from file.
@ -14,68 +14,70 @@ const SERVER_URL = 'http://127.0.0.1:8082';
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './e2e',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: SERVER_URL,
testDir: "./e2e",
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: SERVER_URL,
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry"
},
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] }
}
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Run your local dev server before starting the tests */
webServer: !process.env.TRILIUM_DOCKER ? {
command: 'npm run test:integration-mem-db-dev',
url: SERVER_URL,
reuseExistingServer: !process.env.CI,
} : undefined,
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
webServer: !process.env.TRILIUM_DOCKER
? {
command: "npm run test:integration-mem-db-dev",
url: SERVER_URL,
reuseExistingServer: !process.env.CI
}
: undefined
});

View File

@ -6,4 +6,4 @@ etapi.describeEtapi("app_info", () => {
expect(appInfo.clipperProtocolVersion).toEqual("1.0");
});
});
*/
*/

View File

@ -7,4 +7,4 @@ etapi.describeEtapi("backup", () => {
expect(response.status).toEqual(204);
});
});
*/
*/

View File

@ -23,4 +23,4 @@ etapi.describeEtapi("import", () => {
expect(content).toContain("test export content");
});
});
*/
*/

View File

@ -100,4 +100,4 @@ etapi.describeEtapi("notes", () => {
expect(error.message).toEqual(`Note '${note.noteId}' not found.`);
});
});
*/
*/

View File

@ -14,7 +14,7 @@ export default class BTask extends AbstractBeccaEntity<BOption> {
}
static get hashedProperties() {
return [ "taskId", "parentNoteId", "title", "dueDate", "isDone", "isDeleted" ];
return ["taskId", "parentNoteId", "title", "dueDate", "isDone", "isDeleted"];
}
taskId?: string;

View File

@ -62,7 +62,7 @@ export interface NoteCommandData extends CommandData {
}
export interface ExecuteCommandData<T> extends CommandData {
resolve: (data: T) => void
resolve: (data: T) => void;
}
/**
@ -70,7 +70,7 @@ export interface ExecuteCommandData<T> extends CommandData {
*/
export type CommandMappings = {
"api-log-messages": CommandData;
focusTree: CommandData,
focusTree: CommandData;
focusOnTitle: CommandData;
focusOnDetail: CommandData;
focusOnSearchDefinition: Required<CommandData>;
@ -108,7 +108,7 @@ export type CommandMappings = {
showInfoDialog: ConfirmWithMessageOptions;
showConfirmDialog: ConfirmWithMessageOptions;
showRecentChanges: CommandData & { ancestorNoteId: string };
showImportDialog: CommandData & { noteId: string; };
showImportDialog: CommandData & { noteId: string };
openNewNoteSplit: NoteCommandData;
openInWindow: NoteCommandData;
openNoteInNewTab: CommandData;
@ -131,9 +131,11 @@ export type CommandMappings = {
editNoteTitle: ContextMenuCommandData;
protectSubtree: ContextMenuCommandData;
unprotectSubtree: ContextMenuCommandData;
openBulkActionsDialog: ContextMenuCommandData | {
selectedOrActiveNoteIds?: string[]
};
openBulkActionsDialog:
| ContextMenuCommandData
| {
selectedOrActiveNoteIds?: string[];
};
editBranchPrefix: ContextMenuCommandData;
convertNoteToAttachment: ContextMenuCommandData;
duplicateSubtree: ContextMenuCommandData;
@ -221,11 +223,11 @@ export type CommandMappings = {
moveTabToNewWindow: CommandData;
copyTabToNewWindow: CommandData;
closeActiveTab: CommandData & {
$el: JQuery<HTMLElement>
},
$el: JQuery<HTMLElement>;
};
setZoomFactorAndSave: {
zoomFactor: string;
}
};
reEvaluateRightPaneVisibility: CommandData;
runActiveNote: CommandData;
@ -234,18 +236,18 @@ export type CommandMappings = {
};
scrollToEnd: CommandData;
closeThisNoteSplit: CommandData;
moveThisNoteSplit: CommandData & { isMovingLeft: boolean; };
moveThisNoteSplit: CommandData & { isMovingLeft: boolean };
// Geomap
deleteFromMap: { noteId: string },
openGeoLocation: { noteId: string, event: JQuery.MouseDownEvent }
deleteFromMap: { noteId: string };
openGeoLocation: { noteId: string; event: JQuery.MouseDownEvent };
toggleZenMode: CommandData;
updateAttributeList: CommandData & { attributes: Attribute[] };
saveAttributes: CommandData;
reloadAttributes: CommandData;
refreshNoteList: CommandData & { noteId: string; };
refreshNoteList: CommandData & { noteId: string };
refreshResults: {};
refreshSearchDefinition: {};
@ -348,7 +350,7 @@ type EventMappings = {
ntxId: string | null | undefined; // TODO: deduplicate ntxId
};
tabReorder: {
ntxIdsInOrder: string[]
ntxIdsInOrder: string[];
};
refreshNoteList: {
noteId: string;

View File

@ -37,7 +37,25 @@ const NOTE_TYPE_ICONS = {
* end user. Those types should be used only for checking against, they are
* not for direct use.
*/
export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap" | "geoMap" | "taskList";
export type NoteType =
| "file"
| "image"
| "search"
| "noteMap"
| "launcher"
| "doc"
| "contentWidget"
| "text"
| "relationMap"
| "render"
| "canvas"
| "mermaid"
| "book"
| "webView"
| "code"
| "mindMap"
| "geoMap"
| "taskList";
export interface NotePathRecord {
isArchived: boolean;

View File

@ -264,7 +264,7 @@ export default class DesktopLayout {
.child(new InfoDialog())
.child(new ConfirmDialog())
.child(new PromptDialog())
.child(new CloseZenButton())
.child(new CloseZenButton());
}
#buildLauncherPane(isHorizontal) {

View File

@ -4,7 +4,7 @@ import "../stylesheets/bootstrap.scss";
// Required for correct loading of scripts in Electron
if (typeof module === 'object') {window.module = module; module = undefined;}
const device = getDeviceType()
const device = getDeviceType();
console.log("Setting device cookie to:", device);
setCookie("trilium-device", device);
@ -12,21 +12,21 @@ function setCookie(name: string, value?: string) {
const date = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000);
const expires = "; expires=" + date.toUTCString();
document.cookie = name + "=" + (value || "") + expires + "; path=/";
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
function getDeviceType() {
if (window.location.search === '?desktop') return "desktop";
if (window.location.search === '?mobile') return "mobile";
if (window.location.search === "?desktop") return "desktop";
if (window.location.search === "?mobile") return "mobile";
return isMobile() ? "mobile" : "desktop";
}
// https://stackoverflow.com/a/73731646/944162
function isMobile() {
const mQ = matchMedia?.('(pointer:coarse)');
if (mQ?.media === '(pointer:coarse)') return !!mQ.matches;
const mQ = matchMedia?.("(pointer:coarse)");
if (mQ?.media === "(pointer:coarse)") return !!mQ.matches;
if ('orientation' in window) return true;
const userAgentsRegEx = /\b(Android|iPhone|iPad|iPod|Windows Phone|BlackBerry|webOS|IEMobile)\b/i
return userAgentsRegEx.test(navigator.userAgent)
}
if ("orientation" in window) return true;
const userAgentsRegEx = /\b(Android|iPhone|iPad|iPod|Windows Phone|BlackBerry|webOS|IEMobile)\b/i;
return userAgentsRegEx.test(navigator.userAgent);
}

View File

@ -34,8 +34,8 @@ export default class LauncherContextMenu implements SelectMenuItemEventListener<
const isVisibleRoot = note?.noteId === "_lbVisibleLaunchers";
const isAvailableRoot = note?.noteId === "_lbAvailableLaunchers";
const isVisibleItem = (parentNoteId === "_lbVisibleLaunchers" || parentNoteId === "_lbMobileVisibleLaunchers");
const isAvailableItem = (parentNoteId === "_lbAvailableLaunchers" || parentNoteId === "_lbMobileAvailableLaunchers");
const isVisibleItem = parentNoteId === "_lbVisibleLaunchers" || parentNoteId === "_lbMobileVisibleLaunchers";
const isAvailableItem = parentNoteId === "_lbAvailableLaunchers" || parentNoteId === "_lbMobileAvailableLaunchers";
const isItem = isVisibleItem || isAvailableItem;
const canBeDeleted = !note?.noteId.startsWith("_"); // fixed notes can't be deleted
const canBeReset = !canBeDeleted && note?.isLaunchBarConfig();

View File

@ -1,7 +1,6 @@
import { describe, it, expect } from "vitest";
import attributeParser from "./attribute_parser.js";
describe("Lexing", () => {
it("simple label", () => {
expect(attributeParser.lex("#label").map((t: any) => t.text)).toEqual(["#label"]);
@ -41,7 +40,7 @@ describe("Lexing", () => {
});
describe.todo("Parser", () => {
/* #TODO
/* #TODO
it("simple label", () => {
const attrs = attributeParser.parse(["#token"].map((t: any) => ({ text: t })));

View File

@ -79,7 +79,7 @@ async function renderAttributes(attributes: FAttribute[], renderIsInheritable: b
return $container;
}
const HIDDEN_ATTRIBUTES = [ "originalFileName", "fileSize", "template", "inherit", "cssClass", "iconClass", "pageSize", "viewType", "geolocation", "docName" ];
const HIDDEN_ATTRIBUTES = ["originalFileName", "fileSize", "template", "inherit", "cssClass", "iconClass", "pageSize", "viewType", "geolocation", "docName"];
async function renderNormalAttributes(note: FNote) {
const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes();

View File

@ -99,8 +99,8 @@ const HIGHLIGHT_JS: Library = {
};
const LEAFLET: Library = {
css: [ "node_modules/leaflet/dist/leaflet.css" ],
}
css: ["node_modules/leaflet/dist/leaflet.css"]
};
async function requireLibrary(library: Library) {
if (library.css) {

View File

@ -274,8 +274,8 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent
const { notePath, viewScope } = parseNavigationStateFromUrl(hrefLink);
const ctrlKey = utils.isCtrlKey(evt);
const isLeftClick = ("which" in evt && evt.which === 1);
const isMiddleClick = ("which" in evt && evt.which === 2);
const isLeftClick = "which" in evt && evt.which === 1;
const isMiddleClick = "which" in evt && evt.which === 2;
const openInNewTab = (isLeftClick && ctrlKey) || isMiddleClick;
if (notePath) {

View File

@ -18,7 +18,7 @@ export default class NoteListRenderer {
parentNote,
noteIds,
showNotePath
}
};
if (this.viewType === "list" || this.viewType === "grid") {
this.viewMode = new ListOrGridView(this.viewType, args);

View File

@ -147,11 +147,11 @@ async function renderTooltip(note: FNote | null) {
tooltip: true,
trim: true
});
const isContentEmpty = ($renderedContent[0].innerHTML.length === 0);
const isContentEmpty = $renderedContent[0].innerHTML.length === 0;
let content = "";
if (noteTitleWithPathAsSuffix) {
const classes = [ "note-tooltip-title" ];
const classes = ["note-tooltip-title"];
if (isContentEmpty) {
classes.push("note-no-content");
}

View File

@ -33,20 +33,20 @@ function parseDate(str: string) {
// Source: https://stackoverflow.com/a/30465299/4898894
function getMonthsInDateRange(startDate: string, endDate: string) {
const start = startDate.split('-');
const end = endDate.split('-');
const start = startDate.split("-");
const end = endDate.split("-");
const startYear = parseInt(start[0]);
const endYear = parseInt(end[0]);
const dates = [];
for (let i = startYear; i <= endYear; i++) {
const endMonth = i != endYear ? 11 : parseInt(end[1]) - 1;
const startMon = i === startYear ? parseInt(start[1])-1 : 0;
const startMon = i === startYear ? parseInt(start[1]) - 1 : 0;
for(let j = startMon; j <= endMonth; j = j > 12 ? j % 12 || 11 : j+1) {
const month = j+1;
const displayMonth = month < 10 ? '0'+month : month;
dates.push([i, displayMonth].join('-'));
for (let j = startMon; j <= endMonth; j = j > 12 ? j % 12 || 11 : j + 1) {
const month = j + 1;
const displayMonth = month < 10 ? "0" + month : month;
dates.push([i, displayMonth].join("-"));
}
}
return dates;
@ -161,7 +161,7 @@ function escapeHtml(str: string) {
}
export function escapeQuotes(value: string) {
return value.replaceAll("\"", "&quot;");
return value.replaceAll('"', "&quot;");
}
function formatSize(size: number) {

View File

@ -32,4 +32,4 @@ document.addEventListener(
// add fetchNote as property to the window object
Object.defineProperty(window, "fetchNote", {
value: fetchNote
});
});

View File

@ -11,7 +11,7 @@
interface JQueryStatic {
ui: JQueryUI.UI;
};
}
declare namespace JQueryUI {
interface UI {
@ -679,13 +679,13 @@ declare namespace Fancytree {
activate = 1,
expand = 2,
activate_and_expand = 3,
activate_dblclick_expands = 4,
activate_dblclick_expands = 4
}
enum FancytreeSelectMode {
single = 1,
multi = 2,
mutlti_hier = 3,
mutlti_hier = 3
}
/** Context object passed to events and hook functions. */

View File

@ -13,7 +13,7 @@ declare module "draggabilly" {
containment: HTMLElement
});
element: HTMLElement;
on(event: "pointerDown" | "dragStart" | "dragEnd" | "dragMove", callback: Callback)
on(event: "pointerDown" | "dragStart" | "dragEnd" | "dragMove", callback: Callback);
dragEnd();
isDragging: boolean;
positionDrag: () => void;
@ -21,6 +21,6 @@ declare module "draggabilly" {
}
}
declare module '@mind-elixir/node-menu' {
declare module "@mind-elixir/node-menu" {
export default mindmap;
}

View File

@ -41,7 +41,7 @@ export default class BookmarkButtons extends FlexContainer<Component> {
: new OpenNoteButtonWidget(note).class("launcher-button");
if (this.settings.titlePlacement) {
if (!('settings' in buttonWidget)) {
if (!("settings" in buttonWidget)) {
(buttonWidget as any).settings = {};
}

View File

@ -8,7 +8,7 @@ import appContext from "../../components/app_context.js";
import openService from "../../services/open.js";
import utils from "../../services/utils.js";
import { Dropdown } from "bootstrap";
import type attachmentsApiRoute from "../../../../routes/api/attachments.js"
import type attachmentsApiRoute from "../../../../routes/api/attachments.js";
import type FAttachment from "../../entities/fattachment.js";
import type AttachmentDetailWidget from "../attachment_detail.js";
@ -105,8 +105,7 @@ export default class AttachmentActionsWidget extends BasicWidget {
this.$uploadNewRevisionInput = this.$widget.find(".attachment-upload-new-revision-input");
this.$uploadNewRevisionInput.on("change", async () => {
const fileToUpload = this.$uploadNewRevisionInput[0].files?.item(0); // copy to allow reset below
const fileToUpload = this.$uploadNewRevisionInput[0].files?.item(0); // copy to allow reset below
this.$uploadNewRevisionInput.val("");
if (fileToUpload) {
const result = await server.upload(`attachments/${this.attachmentId}/file`, fileToUpload);
@ -131,7 +130,6 @@ export default class AttachmentActionsWidget extends BasicWidget {
const $openAttachmentCustomButton = this.$widget.find("[data-trigger-command='openAttachmentCustom']");
$openAttachmentCustomButton.addClass("disabled").append($('<span class="bx bx-info-circle disabled-tooltip" />').attr("title", t("attachments_actions.open_custom_client_only")));
}
}
async openAttachmentCommand() {
@ -170,7 +168,6 @@ export default class AttachmentActionsWidget extends BasicWidget {
return;
}
const { note: newNote } = await server.post<ReturnType<typeof attachmentsApiRoute.convertAttachmentToNote>>(`attachments/${this.attachmentId}/convert-to-note`);
toastService.showMessage(t("attachments_actions.convert_success", { title: this.attachment.title }));
await ws.waitForMaxKnownEntityChangeId();

View File

@ -139,7 +139,8 @@ export default class RibbonContainer extends NoteContextAwareWidget {
return super.isEnabled() && this.noteContext?.viewScope?.viewMode === "default";
}
ribbon(widget: NoteContextAwareWidget) { // TODO: Base class
ribbon(widget: NoteContextAwareWidget) {
// TODO: Base class
super.child(widget);
this.ribbonWidgets.push(widget);
@ -236,7 +237,12 @@ export default class RibbonContainer extends NoteContextAwareWidget {
const $ribbonTitle = $('<div class="ribbon-tab-title">')
.attr("data-ribbon-component-id", ribbonWidget.componentId)
.attr("data-ribbon-component-name", (ribbonWidget as any).name as string) // TODO: base class for ribbon widgets
.append($('<span class="ribbon-tab-title-icon">').addClass(ret.icon).attr("title", ret.title).attr("data-toggle-command", (ribbonWidget as any).toggleCommand)) // TODO: base class
.append(
$('<span class="ribbon-tab-title-icon">')
.addClass(ret.icon)
.attr("title", ret.title)
.attr("data-toggle-command", (ribbonWidget as any).toggleCommand)
) // TODO: base class
.append(" ")
.append($('<span class="ribbon-tab-title-label">').text(ret.title));

View File

@ -250,7 +250,7 @@ ws.subscribeToMessages(async (message) => {
message: message,
icon: "arrow-square-up-right"
};
};
}
if (message.taskType !== "export") {
return;

View File

@ -320,7 +320,8 @@ export default class RevisionsDialog extends BasicWidget {
// as a URL to be used in a note. Instead, if they copy and paste it into a note, it will be uploaded as a new note
.attr("src", `data:${fullRevision.mime};base64,${fullRevision.content}`)
.css("max-width", "100%")
.css("max-height", "100%").html()
.css("max-height", "100%")
.html()
);
}
} else if (revisionItem.type === "file") {

View File

@ -1,5 +1,5 @@
import { t } from "../../services/i18n.js";
import NoteContextAwareWidget from "../note_context_aware_widget.js"
import NoteContextAwareWidget from "../note_context_aware_widget.js";
const TPL = `\
<div class="geo-map-buttons">

View File

@ -51,7 +51,7 @@ export default class ContextualHelpButton extends NoteContextAwareWidget {
if (this.note && this.note.type !== "book" && byNoteType[this.note.type]) {
this.helpNoteIdToOpen = byNoteType[this.note.type];
} else if (this.note && this.note.type === "book") {
this.helpNoteIdToOpen = byBookType[this.note.getAttributeValue("label", "viewType") as ViewTypeOptions ?? ""]
this.helpNoteIdToOpen = byBookType[(this.note.getAttributeValue("label", "viewType") as ViewTypeOptions) ?? ""];
}
return !!this.helpNoteIdToOpen;
@ -64,7 +64,7 @@ export default class ContextualHelpButton extends NoteContextAwareWidget {
const targetNote = `_help_${this.helpNoteIdToOpen}`;
const helpSubcontext = subContexts.find((s) => s.viewScope?.viewMode === "contextual-help");
const viewScope: ViewScope = {
viewMode: "contextual-help",
viewMode: "contextual-help"
};
if (!helpSubcontext) {
// The help is not already open, open a new split with it.
@ -74,7 +74,7 @@ export default class ContextualHelpButton extends NoteContextAwareWidget {
notePath: targetNote,
hoistedNoteId: "_help",
viewScope
})
});
} else {
// There is already a help window open, make sure it opens on the right note.
helpSubcontext.setNote(targetNote, { viewScope });

View File

@ -157,7 +157,7 @@ export default class BacklinksWidget extends NoteContextAwareWidget {
if (backlink.relationName) {
$item.append($("<p>").text(`${t("zpetne_odkazy.relation")}: ${backlink.relationName}`));
} else {
$item.append(...backlink.excerpts ?? []);
$item.append(...(backlink.excerpts ?? []));
}
this.$items.append($item);

View File

@ -19,10 +19,10 @@ const TPL = `\
</style>
<div class="geo-map-container"></div>
</div>`
</div>`;
export type Leaflet = typeof import("leaflet");
export type InitCallback = ((L: Leaflet) => void);
export type InitCallback = (L: Leaflet) => void;
export default class GeoMapWidget extends NoteContextAwareWidget {
@ -40,24 +40,23 @@ export default class GeoMapWidget extends NoteContextAwareWidget {
this.$container = this.$widget.find(".geo-map-container");
library_loader.requireLibrary(library_loader.LEAFLET)
.then(async () => {
const L = (await import("leaflet")).default;
library_loader.requireLibrary(library_loader.LEAFLET).then(async () => {
const L = (await import("leaflet")).default;
const map = L.map(this.$container[0], {
worldCopyJump: true
});
this.map = map;
if (this.initCallback) {
this.initCallback(L);
}
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
detectRetina: true
}).addTo(map);
const map = L.map(this.$container[0], {
worldCopyJump: true
});
this.map = map;
if (this.initCallback) {
this.initCallback(L);
}
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
detectRetina: true
}).addTo(map);
});
}
}

View File

@ -66,7 +66,7 @@ export default class MermaidWidget extends NoteContextAwareWidget {
mermaid.mermaidAPI.initialize({
startOnLoad: false,
...getMermaidConfig() as any
...(getMermaidConfig() as any)
});
this.$display.empty();

View File

@ -81,7 +81,16 @@ const typeWidgetClasses = {
* A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one,
* for protected session or attachment information.
*/
type ExtendedNoteType = Exclude<NoteType, "mermaid" | "launcher" | "text" | "code"> | "empty" | "readOnlyCode" | "readOnlyText" | "editableText" | "editableCode" | "attachmentDetail" | "attachmentList" | "protectedSession";
type ExtendedNoteType =
| Exclude<NoteType, "mermaid" | "launcher" | "text" | "code">
| "empty"
| "readOnlyCode"
| "readOnlyText"
| "editableText"
| "editableCode"
| "attachmentDetail"
| "attachmentList"
| "protectedSession";
export default class NoteDetailWidget extends NoteContextAwareWidget {
@ -329,7 +338,9 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
const label = attrs.find(
(attr) =>
attr.type === "label" && ["readOnly", "autoReadOnlyDisabled", "cssClass", "displayRelations", "hideRelations"].includes(attr.name ?? "") && attributeService.isAffecting(attr, this.note)
attr.type === "label" &&
["readOnly", "autoReadOnlyDisabled", "cssClass", "displayRelations", "hideRelations"].includes(attr.name ?? "") &&
attributeService.isAffecting(attr, this.note)
);
const relation = attrs.find((attr) => attr.type === "relation" && ["template", "inherit", "renderNote"].includes(attr.name ?? "") && attributeService.isAffecting(attr, this.note));

View File

@ -139,7 +139,7 @@ interface NotesAndRelationsData {
source: string;
target: string;
name: string;
}[]
}[];
}
// Replace
@ -152,7 +152,7 @@ interface ResponseLink {
interface PostNotesMapResponse {
notes: string[];
links: ResponseLink[],
links: ResponseLink[];
noteIdToDescendantCountMap: Record<string, number>;
}
@ -160,7 +160,7 @@ interface GroupedLink {
id: string;
sourceNoteId: string;
targetNoteId: string;
names: string[]
names: string[];
}
interface CssData {
@ -313,9 +313,7 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
ctx.fillStyle = color;
ctx.beginPath();
if (node.x && node.y) {
ctx.arc(node.x, node.y,
this.noteIdToSizeMap[node.id], 0,
2 * Math.PI, false);
ctx.arc(node.x, node.y, this.noteIdToSizeMap[node.id], 0, 2 * Math.PI, false);
}
ctx.fill();
})
@ -467,13 +465,13 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
}
if (source.x && source.y && target.x && target.y) {
const x = ((source.x) + (target.x)) / 2;
const y = ((source.y) + (target.y)) / 2;
const x = (source.x + target.x) / 2;
const y = (source.y + target.y) / 2;
ctx.save();
ctx.translate(x, y);
const deltaY = (source.y) - (target.y);
const deltaX = (source.x) - (target.x);
const deltaY = source.y - target.y;
const deltaX = source.x - target.x;
let angle = Math.atan2(deltaY, deltaX);
let moveY = 2;

View File

@ -91,7 +91,7 @@ export default class NoteTitleWidget extends NoteContextAwareWidget {
async refreshWithNote(note: FNote) {
const isReadOnly = (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) || utils.isLaunchBarConfig(note.noteId) || this.noteContext?.viewScope?.viewMode !== "default";
this.$noteTitle.val(isReadOnly ? await this.noteContext?.getNavigationTitle() || "" : note.title);
this.$noteTitle.val(isReadOnly ? (await this.noteContext?.getNavigationTitle()) || "" : note.title);
this.$noteTitle.prop("readonly", isReadOnly);
this.setProtectedStatus(note);

View File

@ -159,11 +159,11 @@ interface CreateLauncherResponse {
message: string;
note: {
noteId: string;
}
};
}
interface ExpandedSubtreeResponse {
branchIds: string[]
branchIds: string[];
}
interface Node extends Fancytree.NodeData {
@ -180,7 +180,6 @@ interface RefreshContext {
}
export default class NoteTreeWidget extends NoteContextAwareWidget {
private $tree!: JQuery<HTMLElement>;
private $treeActions!: JQuery<HTMLElement>;
private $treeSettingsButton!: JQuery<HTMLElement>;
@ -571,10 +570,13 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
clones: {
highlightActiveClones: true
},
enhanceTitle: async function (event: Event, data: {
node: Fancytree.FancytreeNode;
noteId: string;
}) {
enhanceTitle: async function (
event: Event,
data: {
node: Fancytree.FancytreeNode;
noteId: string;
}
) {
const node = data.node;
if (!node.data.noteId) {

View File

@ -9,7 +9,7 @@ export default class ProtectedNoteSwitchWidget extends SwitchWidget {
super.doRender();
this.switchOnName = t("protect_note.toggle-on");
this.switchOnTooltip = t("protect_note.toggle-on-hint");
this.switchOnTooltip = t("protect_note.toggle-on-hint");
this.switchOffName = t("protect_note.toggle-off");
this.switchOffTooltip = t("protect_note.toggle-off-hint");

View File

@ -126,7 +126,7 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
return;
}
if (![ "list", "grid", "calendar"].includes(type)) {
if (!["list", "grid", "calendar"].includes(type)) {
throw new Error(t("book_properties.invalid_view_type", { type }));
}

View File

@ -122,7 +122,7 @@ export default class NoteMapRibbonWidget extends NoteContextAwareWidget {
const { top } = this.$widget[0].getBoundingClientRect();
const height = ($(window).height() ?? 0) - top;
const width = (this.$widget.width() ?? 0);
const width = this.$widget.width() ?? 0;
this.$widget.find(".note-map-container")
.height(height)

View File

@ -135,8 +135,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.getBranchRows().find((branch) => branch.noteId === this.noteId) ||
(this.noteId != null && loadResults.isNoteReloaded(this.noteId))) {
if (loadResults.getBranchRows().find((branch) => branch.noteId === this.noteId) || (this.noteId != null && loadResults.isNoteReloaded(this.noteId))) {
this.refresh();
}
}

View File

@ -46,6 +46,9 @@ export default class NotePropertiesWidget extends NoteContextAwareWidget {
async refreshWithNote(note: FNote) {
const pageUrl = note.getLabelValue("pageUrl");
this.$pageUrl.attr("href", pageUrl).attr("title", pageUrl).text(pageUrl ?? "");
this.$pageUrl
.attr("href", pageUrl)
.attr("title", pageUrl)
.text(pageUrl ?? "");
}
}

View File

@ -40,7 +40,6 @@ interface SimilarNote {
noteId: string;
}
export default class SimilarNotesWidget extends NoteContextAwareWidget {
private $similarNotesWrapper!: JQuery<HTMLElement>;

View File

@ -185,7 +185,7 @@ export default class SwitchWidget extends NoteContextAwareWidget {
/** Gets or sets whether the switch is enabled. */
get canToggle() {
return (!this.$switchButton.hasClass("disabled"));
return !this.$switchButton.hasClass("disabled");
}
set canToggle(isEnabled) {

View File

@ -78,8 +78,8 @@ export default class SyncStatusWidget extends BasicWidget {
lastSyncedPush!: number;
settings: {
// TriliumNextTODO: narrow types and use TitlePlacement Type
titlePlacement: string;
}
titlePlacement: string;
};
constructor() {
super();
@ -106,7 +106,6 @@ export default class SyncStatusWidget extends BasicWidget {
return;
}
Tooltip.getOrCreateInstance(this.$widget.find(`.sync-status-${className}`)[0], {
html: true,
placement: this.settings.titlePlacement,

View File

@ -15,7 +15,7 @@ const TAB_CONTAINER_MIN_WIDTH = 24;
const TAB_CONTAINER_MAX_WIDTH = 240;
const TAB_CONTAINER_LEFT_PADDING = 5;
const NEW_TAB_WIDTH = 32;
const MIN_FILLER_WIDTH = (isDesktop ? 50 : 15);
const MIN_FILLER_WIDTH = isDesktop ? 50 : 15;
const MARGIN_WIDTH = 5;
const TAB_SIZE_SMALL = 84;

View File

@ -17,10 +17,10 @@ export default class TemplateSwitchWidget extends SwitchWidget {
super.doRender();
this.switchOnName = t("template_switch.template");
this.switchOnTooltip = t("template_switch.toggle-on-hint");
this.switchOnTooltip = t("template_switch.toggle-on-hint");
this.switchOffName = t("template_switch.template");
this.switchOffTooltip = t("template_switch.toggle-off-hint");
this.switchOffTooltip = t("template_switch.toggle-off-hint");
this.$helpButton.attr("data-help-page", "template.html").show();
}

View File

@ -55,8 +55,8 @@ const TPL = `<div class="toc-widget">
</div>`;
interface Toc {
$toc: JQuery<HTMLElement>,
headingCount: number
$toc: JQuery<HTMLElement>;
headingCount: number;
}
export default class TocWidget extends RightPanelWidget {
@ -89,8 +89,8 @@ export default class TocWidget extends RightPanelWidget {
return false;
}
const isHelpNote = (this.note.type === "doc" && this.note.noteId.startsWith("_help"));
const isTextNote = (this.note.type === "text");
const isHelpNote = this.note.type === "doc" && this.note.noteId.startsWith("_help");
const isTextNote = this.note.type === "text";
const isNoteSupported = isTextNote || isHelpNote;
return isNoteSupported
@ -156,7 +156,7 @@ export default class TocWidget extends RightPanelWidget {
const tocLabelValue = this.tocLabelValue;
const visible = (tocLabelValue === "" || tocLabelValue === "show") || headingCount >= (options.getInt("minTocHeadings") ?? 0);
const visible = tocLabelValue === "" || tocLabelValue === "show" || headingCount >= (options.getInt("minTocHeadings") ?? 0);
this.toggleInt(visible);
if (this.noteContext?.viewScope) {
this.noteContext.viewScope.tocPreviousVisible = visible;

View File

@ -63,7 +63,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
this.$linksWrapper.empty().append(
t("attachment_detail.owning_note"),
(await linkService.createLink(this.noteId)),
await linkService.createLink(this.noteId),
t("attachment_detail.you_can_also_open"),
await linkService.createLink(this.noteId, {
title: t("attachment_detail.list_of_all_attachments"),
@ -74,7 +74,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
$helpButton
);
const attachment = (this.attachmentId) ? await froca.getAttachment(this.attachmentId, true) : null;
const attachment = this.attachmentId ? await froca.getAttachment(this.attachmentId, true) : null;
if (!attachment) {
this.$wrapper.html("<strong>" + t("attachment_detail.attachment_deleted") + "</strong>");

View File

@ -66,7 +66,7 @@ export default class AttachmentListTypeWidget extends TypeWidget {
.text(t("attachment_list.upload_attachments"))
.on("click", () => {
if (this.noteId) {
this.triggerCommand("showUploadAttachmentsDialog", { noteId: this.noteId })
this.triggerCommand("showUploadAttachmentsDialog", { noteId: this.noteId });
}
}),
$helpButton

View File

@ -36,9 +36,7 @@ export default class BookTypeWidget extends TypeWidget {
}
async doRefresh(note: FNote) {
this.$helpNoChildren.toggle(
!this.note?.hasChildren()
&& this.note?.getAttributeValue("label", "viewType") !== "calendar");
this.$helpNoChildren.toggle(!this.note?.hasChildren() && this.note?.getAttributeValue("label", "viewType") !== "calendar");
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {

View File

@ -59,9 +59,9 @@ const TPL = `
`;
interface CanvasContent {
elements: ExcalidrawElement[],
files: BinaryFileData[],
appState: Partial<AppState>
elements: ExcalidrawElement[];
files: BinaryFileData[];
appState: Partial<AppState>;
}
interface AttachmentMetadata {
@ -198,7 +198,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
}
(window.process.env as any).PREACT = false;
const excalidraw = (await import("@excalidraw/excalidraw"));
const excalidraw = await import("@excalidraw/excalidraw");
this.excalidrawLib = excalidraw;
const { createRoot } = await import("react-dom/client");
@ -476,7 +476,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
createExcalidrawReactApp(react: typeof React, excalidrawComponent: React.MemoExoticComponent<(props: ExcalidrawProps) => JSX.Element>) {
const excalidrawWrapperRef = react.useRef<HTMLElement>(null);
this.excalidrawWrapperRef = excalidrawWrapperRef;
const [dimensions, setDimensions] = react.useState<{ width?: number, height?: number}>({
const [dimensions, setDimensions] = react.useState<{ width?: number; height?: number }>({
width: undefined,
height: undefined
});

View File

@ -7,91 +7,78 @@ export function buildConfig() {
image: {
styles: {
options: [
'inline',
'alignBlockLeft',
'alignCenter',
'alignBlockRight',
'alignLeft',
'alignRight',
'full', // full and side are for BC since the old images have been created with these styles
'side'
"inline",
"alignBlockLeft",
"alignCenter",
"alignBlockRight",
"alignLeft",
"alignRight",
"full", // full and side are for BC since the old images have been created with these styles
"side"
]
},
resizeOptions: [
{
name: 'imageResize:original',
name: "imageResize:original",
value: null,
icon: 'original'
icon: "original"
},
{
name: 'imageResize:25',
value: '25',
icon: 'small'
name: "imageResize:25",
value: "25",
icon: "small"
},
{
name: 'imageResize:50',
value: '50',
icon: 'medium'
name: "imageResize:50",
value: "50",
icon: "medium"
},
{
name: 'imageResize:75',
value: '75',
icon: 'medium'
name: "imageResize:75",
value: "75",
icon: "medium"
}
],
toolbar: [
// Image styles, see https://ckeditor.com/docs/ckeditor5/latest/features/images/images-styles.html#demo.
'imageStyle:inline',
'imageStyle:alignCenter',
"imageStyle:inline",
"imageStyle:alignCenter",
{
name: "imageStyle:wrapText",
title: "Wrap text",
items: [
'imageStyle:alignLeft',
'imageStyle:alignRight',
],
defaultItem: 'imageStyle:alignRight'
items: ["imageStyle:alignLeft", "imageStyle:alignRight"],
defaultItem: "imageStyle:alignRight"
},
{
name: "imageStyle:block",
title: "Block align",
items: [
'imageStyle:alignBlockLeft',
'imageStyle:alignBlockRight'
],
defaultItem: "imageStyle:alignBlockLeft",
items: ["imageStyle:alignBlockLeft", "imageStyle:alignBlockRight"],
defaultItem: "imageStyle:alignBlockLeft"
},
'|',
'imageResize:25',
'imageResize:50',
'imageResize:original',
'|',
'toggleImageCaption'
"|",
"imageResize:25",
"imageResize:50",
"imageResize:original",
"|",
"toggleImageCaption"
],
upload: {
types: [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff', 'svg', 'svg+xml', 'avif' ]
types: ["jpeg", "png", "gif", "bmp", "webp", "tiff", "svg", "svg+xml", "avif"]
}
},
heading: {
options: [
{ model: 'paragraph' as const, title: 'Paragraph', class: 'ck-heading_paragraph' },
{ model: "paragraph" as const, title: "Paragraph", class: "ck-heading_paragraph" },
// // heading1 is not used since that should be a note's title
{ model: 'heading2' as const, view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
{ model: 'heading3' as const, view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
{ model: 'heading4' as const, view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
{ model: 'heading5' as const, view: 'h5', title: 'Heading 5', class: 'ck-heading_heading5' },
{ model: 'heading6' as const, view: 'h6', title: 'Heading 6', class: 'ck-heading_heading6' }
{ model: "heading2" as const, view: "h2", title: "Heading 2", class: "ck-heading_heading2" },
{ model: "heading3" as const, view: "h3", title: "Heading 3", class: "ck-heading_heading3" },
{ model: "heading4" as const, view: "h4", title: "Heading 4", class: "ck-heading_heading4" },
{ model: "heading5" as const, view: "h5", title: "Heading 5", class: "ck-heading_heading5" },
{ model: "heading6" as const, view: "h6", title: "Heading 6", class: "ck-heading_heading6" }
]
},
table: {
contentToolbar: [
'tableColumn',
'tableRow',
'mergeTableCells',
'tableProperties',
'tableCellProperties',
'toggleTableCaption'
]
contentToolbar: ["tableColumn", "tableRow", "mergeTableCells", "tableProperties", "tableCellProperties", "toggleTableCaption"]
},
list: {
properties: {
@ -101,17 +88,17 @@ export function buildConfig() {
}
},
link: {
defaultProtocol: 'https://',
defaultProtocol: "https://",
allowedProtocols: ALLOWED_PROTOCOLS
},
// This value must be kept in sync with the language defined in webpack.config.js.
language: 'en'
}
language: "en"
};
}
export function buildToolbarConfig(isClassicToolbar: boolean) {
if (isClassicToolbar) {
const multilineToolbar = utils.isDesktop() && options.get("textNoteEditorMultilineToolbar") === "true"
const multilineToolbar = utils.isDesktop() && options.get("textNoteEditorMultilineToolbar") === "true";
return buildClassicToolbar(multilineToolbar);
} else {
return buildFloatingToolbar();
@ -123,101 +110,92 @@ function buildClassicToolbar(multilineToolbar: boolean) {
return {
toolbar: {
items: [
'heading', 'fontSize',
'|',
'bold', 'italic',
"heading",
"fontSize",
"|",
"bold",
"italic",
{
label: "Text formatting",
icon: "text",
items: [
'underline',
'strikethrough',
'superscript',
'subscript',
'code',
],
items: ["underline", "strikethrough", "superscript", "subscript", "code"]
},
'|',
'fontColor', 'fontBackgroundColor', 'removeFormat',
'|',
'bulletedList', 'numberedList', 'todoList',
'|',
'blockQuote', 'insertTable', 'codeBlock', 'footnote',
"|",
"fontColor",
"fontBackgroundColor",
"removeFormat",
"|",
"bulletedList",
"numberedList",
"todoList",
"|",
"blockQuote",
"insertTable",
"codeBlock",
"footnote",
{
label: "Insert",
icon: "plus",
items: [
'imageUpload',
'|',
'link',
'internallink',
'includeNote',
'|',
'specialCharacters',
'math',
'mermaid',
'horizontalLine',
'pageBreak'
]
items: ["imageUpload", "|", "link", "internallink", "includeNote", "|", "specialCharacters", "math", "mermaid", "horizontalLine", "pageBreak"]
},
'|',
'outdent', 'indent',
'|',
'markdownImport', 'cuttonote', 'findAndReplace'
"|",
"outdent",
"indent",
"|",
"markdownImport",
"cuttonote",
"findAndReplace"
],
shouldNotGroupWhenFull: multilineToolbar
}
}
};
}
function buildFloatingToolbar() {
return {
toolbar: {
items: [
'fontSize',
'bold',
'italic',
'underline',
'strikethrough',
'superscript',
'subscript',
'fontColor',
'fontBackgroundColor',
'code',
'link',
'removeFormat',
'internallink',
'cuttonote'
]
},
items: [
"fontSize",
"bold",
"italic",
"underline",
"strikethrough",
"superscript",
"subscript",
"fontColor",
"fontBackgroundColor",
"code",
"link",
"removeFormat",
"internallink",
"cuttonote"
]
},
blockToolbar: [
'heading',
'|',
'bulletedList', 'numberedList', 'todoList',
'|',
'blockQuote', 'codeBlock', 'insertTable',
'footnote',
{
label: "Insert",
icon: "plus",
items: [
'internallink',
'includeNote',
'|',
'math',
'mermaid',
'horizontalLine',
'pageBreak'
]
},
'|',
'outdent', 'indent',
'|',
'imageUpload',
'markdownImport',
'specialCharacters',
'findAndReplace'
]
blockToolbar: [
"heading",
"|",
"bulletedList",
"numberedList",
"todoList",
"|",
"blockQuote",
"codeBlock",
"insertTable",
"footnote",
{
label: "Insert",
icon: "plus",
items: ["internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak"]
},
"|",
"outdent",
"indent",
"|",
"imageUpload",
"markdownImport",
"specialCharacters",
"findAndReplace"
]
};
}

View File

@ -15,7 +15,7 @@ import CodeMimeTypesOptions from "./options/code_notes/code_mime_types.js";
import ImageOptions from "./options/images/images.js";
import SpellcheckOptions from "./options/spellcheck.js";
import PasswordOptions from "./options/password/password.js";
import ProtectedSessionTimeoutOptions from "./options/password/protected_session_timeout.js"
import ProtectedSessionTimeoutOptions from "./options/password/protected_session_timeout.js";
import EtapiOptions from "./options/etapi.js";
import BackupOptions from "./options/backup.js";
import SyncOptions from "./options/sync.js";

View File

@ -1,7 +1,7 @@
import { GPX, Marker, type LatLng, type LeafletMouseEvent } from "leaflet";
import type FNote from "../../entities/fnote.js";
import GeoMapWidget, { type InitCallback, type Leaflet } from "../geo_map.js";
import TypeWidget from "./type_widget.js"
import TypeWidget from "./type_widget.js";
import server from "../../services/server.js";
import toastService from "../../services/toast.js";
import dialogService from "../../services/dialog.js";
@ -75,21 +75,21 @@ const TPL = `\
const LOCATION_ATTRIBUTE = "geolocation";
const CHILD_NOTE_ICON = "bx bx-pin";
const DEFAULT_COORDINATES: [ number, number ] = [ 3.878638227135724, 446.6630455551659 ];
const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659];
const DEFAULT_ZOOM = 2;
interface MapData {
view?: {
center?: LatLng | [ number, number ];
center?: LatLng | [number, number];
zoom?: number;
}
};
}
// TODO: Deduplicate
interface CreateChildResponse {
note: {
noteId: string;
}
};
}
enum State {
@ -220,7 +220,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
return;
}
const [ lat, lng ] = latLng.split(",", 2).map((el) => parseFloat(el));
const [lat, lng] = latLng.split(",", 2).map((el) => parseFloat(el));
const L = this.L;
const icon = this.#buildIcon(note.getIcon(), note.getColorClass(), note.title);
@ -228,10 +228,10 @@ export default class GeoMapTypeWidget extends TypeWidget {
icon,
draggable: true,
autoPan: true,
autoPanSpeed: 5,
autoPanSpeed: 5
})
.addTo(map)
.on("moveend", e => {
.on("moveend", (e) => {
this.moveMarker(note.noteId, (e.target as Marker).getLatLng());
});
marker.on("mousedown", ({ originalEvent }) => {
@ -264,9 +264,9 @@ export default class GeoMapTypeWidget extends TypeWidget {
<img class="icon-shadow" src="${asset_path}/node_modules/leaflet/dist/images/marker-shadow.png" />
<span class="bx ${bxIconClass} ${colorClass}"></span>
<span class="title-label">${title}</span>`,
iconSize: [ 25, 41 ],
iconAnchor: [ 12, 41 ]
})
iconSize: [25, 41],
iconAnchor: [12, 41]
});
}
#changeState(newState: State) {
@ -296,7 +296,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
}
async moveMarker(noteId: string, latLng: LatLng | null) {
const value = (latLng ? [latLng.lat, latLng.lng].join(",") : "");
const value = latLng ? [latLng.lat, latLng.lng].join(",") : "";
await attributes.setLabel(noteId, LOCATION_ATTRIBUTE, value);
}
@ -361,7 +361,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
// If any of note has its location attribute changed.
// TODO: Should probably filter by parent here as well.
const attributeRows = loadResults.getAttributeRows();
if (attributeRows.find((at) => [ LOCATION_ATTRIBUTE, "color" ].includes(at.name ?? ""))) {
if (attributeRows.find((at) => [LOCATION_ATTRIBUTE, "color"].includes(at.name ?? ""))) {
this.#reloadMarkers();
}
}

View File

@ -259,7 +259,7 @@ export default class MindMapWidget extends TypeWidget {
return await this.mind.exportSvg().text();
}
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded"> ) {
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (this.noteId && loadResults.isNoteReloaded(this.noteId)) {
this.refresh();
}

View File

@ -60,7 +60,7 @@ interface Theme {
val: string;
}
type Response = Record<string, Theme[]>
type Response = Record<string, Theme[]>;
/**
* Contains appearance settings for code blocks within text notes, such as the theme for the syntax highlighter.

View File

@ -94,10 +94,12 @@ export default class ThemeOptions extends OptionsWidget {
this.$themeSelect.empty();
for (const theme of themes) {
this.$themeSelect.append($("<option>")
.attr("value", theme.val)
.attr("data-note-id", theme.noteId || "")
.text(theme.title));
this.$themeSelect.append(
$("<option>")
.attr("value", theme.val)
.attr("data-note-id", theme.noteId || "")
.text(theme.title)
);
}
this.$themeSelect.val(options.theme);

View File

@ -97,9 +97,8 @@ export default class CodeMimeTypesOptions extends OptionsWidget {
const checkbox = $(`<label class="tn-checkbox">`)
.append($('<input type="checkbox" class="form-check-input">').attr("id", id).attr("data-mime-type", mimeType.mime).prop("checked", mimeType.enabled))
.on("change", () => this.save())
.append(mimeType.title)
.append(mimeType.title);
return $("<li>")
.append(checkbox);
return $("<li>").append(checkbox);
}
}

View File

@ -31,7 +31,7 @@ export default class NoteErasureTimeoutOptions extends TimeSelector {
const $timeSelector = this.$widget;
// inject TimeSelector widget template
this.$widget = $(TPL);
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector);
this.$eraseDeletedNotesButton = this.$widget.find("#erase-deleted-notes-now-button");

View File

@ -26,6 +26,6 @@ export default class RevisionsSnapshotIntervalOptions extends TimeSelector {
const $timeSelector = this.$widget;
// inject TimeSelector widget template
this.$widget = $(TPL);
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector);
}
}

View File

@ -29,8 +29,8 @@ export default class ShareSettingsOptions extends OptionsWidget {
this.$widget = $(TPL);
this.contentSized();
this.$shareRootCheck = this.$widget.find('.share-root-check');
this.$shareRootStatus = this.$widget.find('.share-root-status');
this.$shareRootCheck = this.$widget.find(".share-root-check");
this.$shareRootStatus = this.$widget.find(".share-root-status");
// Add change handlers for both checkboxes
this.$widget.find('input[type="checkbox"]').on("change", (e: JQuery.ChangeEvent) => {
@ -38,7 +38,7 @@ export default class ShareSettingsOptions extends OptionsWidget {
// Show/hide share root status section based on redirectBareDomain checkbox
const target = e.target as HTMLInputElement;
if (target.name === 'redirectBareDomain') {
if (target.name === "redirectBareDomain") {
this.$shareRootCheck.toggle(target.checked);
if (target.checked) {
this.checkShareRoot();
@ -47,7 +47,7 @@ export default class ShareSettingsOptions extends OptionsWidget {
});
// Add click handler for check share root button
this.$widget.find('.check-share-root').on("click", () => this.checkShareRoot());
this.$widget.find(".check-share-root").on("click", () => this.checkShareRoot());
}
async optionsLoaded(options: OptionMap) {
@ -62,28 +62,26 @@ export default class ShareSettingsOptions extends OptionsWidget {
}
async checkShareRoot() {
const $button = this.$widget.find('.check-share-root');
$button.prop('disabled', true);
const $button = this.$widget.find(".check-share-root");
$button.prop("disabled", true);
try {
const shareRootNotes = await searchService.searchForNotes("#shareRoot");
const sharedShareRootNote = shareRootNotes.find(note => note.isShared());
const sharedShareRootNote = shareRootNotes.find((note) => note.isShared());
if (sharedShareRootNote) {
this.$shareRootStatus
.removeClass('text-danger')
.addClass('text-success')
.text(t("share.share_root_found", {noteTitle: sharedShareRootNote.title}));
.removeClass("text-danger")
.addClass("text-success")
.text(t("share.share_root_found", { noteTitle: sharedShareRootNote.title }));
} else {
this.$shareRootStatus
.removeClass('text-success')
.addClass('text-danger')
.text(shareRootNotes.length > 0
? t("share.share_root_not_shared", {noteTitle: shareRootNotes[0].title})
: t("share.share_root_not_found"));
.removeClass("text-success")
.addClass("text-danger")
.text(shareRootNotes.length > 0 ? t("share.share_root_not_shared", { noteTitle: shareRootNotes[0].title }) : t("share.share_root_not_found"));
}
} finally {
$button.prop('disabled', false);
$button.prop("disabled", false);
}
}

View File

@ -25,6 +25,6 @@ export default class ProtectedSessionTimeoutOptions extends TimeSelector {
const $timeSelector = this.$widget;
// inject TimeSelector widget template
this.$widget = $(TPL);
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector);
}
}

View File

@ -54,7 +54,7 @@ export default class TimeSelector extends OptionsWidget {
this.optionValueId = options.optionValueId;
this.optionTimeScaleId = options.optionTimeScaleId;
this.includedTimeScales = options.includedTimeScales || new Set(["seconds", "minutes", "hours", "days"]);
this.minimumSeconds = options.minimumSeconds || 0
this.minimumSeconds = options.minimumSeconds || 0;
}
doRender() {
@ -131,10 +131,10 @@ export default class TimeSelector extends OptionsWidget {
private setInternalTimeInSeconds(time: number) {
if (time < this.minimumSeconds) {
toastService.showError(t("time_selector.minimum_input", {minimumSeconds: this.minimumSeconds}));
return this.internalTimeInSeconds = this.minimumSeconds;
toastService.showError(t("time_selector.minimum_input", { minimumSeconds: this.minimumSeconds }));
return (this.internalTimeInSeconds = this.minimumSeconds);
}
return this.internalTimeInSeconds = time;
return (this.internalTimeInSeconds = time);
}
}

View File

@ -87,7 +87,7 @@ const TPL = `
`;
function buildTasks(tasks: FTask[]) {
let html = '';
let html = "";
const now = dayjs();
const dateFormat = "DD-MM-YYYY";
@ -137,7 +137,9 @@ export default class TaskListWidget extends TypeWidget {
private $taskContainer!: JQuery<HTMLElement>;
private $addNewTask!: JQuery<HTMLElement>;
static getType() { return "taskList" }
static getType() {
return "taskList";
}
doRender() {
this.$widget = $(TPL);
@ -231,19 +233,18 @@ export default class TaskListWidget extends TypeWidget {
return [];
}
return (await froca.getTasks(this.noteId))
.toSorted((a, b) => {
// Sort by due date, closest date first.
if (!a.dueDate) {
return 1;
}
return (await froca.getTasks(this.noteId)).toSorted((a, b) => {
// Sort by due date, closest date first.
if (!a.dueDate) {
return 1;
}
if (!b.dueDate) {
return -1;
}
if (!b.dueDate) {
return -1;
}
return a.dueDate.localeCompare(b.dueDate, "en");
});
return a.dueDate.localeCompare(b.dueDate, "en");
});
}
async doRefresh(note: FNote) {

View File

@ -68,7 +68,7 @@ const TPL = `
interface CreateChildResponse {
note: {
noteId: string;
}
};
}
export default class CalendarView extends ViewMode {
@ -126,7 +126,7 @@ export default class CalendarView extends ViewMode {
weekNumbers: this.parentNote.hasAttribute("label", "calendar:weekNumbers"),
locale: await CalendarView.#getLocale(),
height: "100%",
eventContent: (e => {
eventContent: (e) => {
let html = "";
const { iconClass, promotedAttributes } = e.event.extendedProps;
@ -138,7 +138,7 @@ export default class CalendarView extends ViewMode {
// Promoted attributes
if (promotedAttributes) {
for (const [ name, value ] of Object.entries(promotedAttributes)) {
for (const [name, value] of Object.entries(promotedAttributes)) {
html += `\
<div class="promoted-attribute">
<span class="promoted-attribute-name">${name}</span>: <span class="promoted-attribute-value">${value}</span>
@ -147,7 +147,7 @@ export default class CalendarView extends ViewMode {
}
return { html };
}),
},
dateClick: async (e) => {
if (!this.isCalendarRoot) {
return;
@ -260,7 +260,7 @@ export default class CalendarView extends ViewMode {
// TODO: Deduplicate get type.
const dateNotesForMonth = await server.get<Record<string, string>>(`special-notes/notes-for-month/${month}?calendarRoot=${this.parentNote.noteId}`);
const dateNoteIds = Object.values(dateNotesForMonth);
allDateNoteIds = [ ...allDateNoteIds, ...dateNoteIds ];
allDateNoteIds = [...allDateNoteIds, ...dateNoteIds];
}
// Request all the date notes.
@ -379,7 +379,7 @@ export default class CalendarView extends ViewMode {
const result: Record<string, string> = {};
for (const promotedAttribute of filteredPromotedAttributes) {
const [ type, name ] = promotedAttribute.name.split(":", 2);
const [type, name] = promotedAttribute.name.split(":", 2);
const definition = promotedAttribute.getDefinition();
if (definition.multiplicity !== "single") {
@ -411,7 +411,7 @@ export default class CalendarView extends ViewMode {
if (customTitleValue.startsWith("#")) {
const labelValue = note.getAttributeValue("label", attributeName);
if (labelValue) {
return [ labelValue ];
return [labelValue];
}
} else if (allowRelations && customTitleValue.startsWith("~")) {
const relations = note.getRelations(attributeName);
@ -432,7 +432,7 @@ export default class CalendarView extends ViewMode {
}
}
return [ note.title ];
return [note.title];
}
static async #setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) {
@ -456,7 +456,7 @@ export default class CalendarView extends ViewMode {
const offset = date.getTimezoneOffset();
const localDate = new Date(date.getTime() - offset * 60 * 1000);
return localDate.toISOString().split('T')[0];
return localDate.toISOString().split("T")[0];
}
static #offsetDate(date: Date | string | null | undefined, offset: number) {

View File

@ -1,2 +1,2 @@
// Import all of Bootstrap's CSS
@import "bootstrap/scss/bootstrap";
@import "bootstrap/scss/bootstrap";

View File

@ -75,7 +75,6 @@
flex-wrap: wrap;
}
.calendar-dropdown-widget .calendar-week span {
flex-direction: column;
flex: 0 0 14.28%;

View File

@ -36,7 +36,7 @@
.note-list-widget,
.spacer {
display: none !important;
}
}
body.mobile #mobile-sidebar-wrapper,
body.mobile .classic-toolbar-widget,
@ -197,14 +197,13 @@ span[style] {
.note-detail-printable .todo-list__label * {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
print-color-adjust: exact;
}
@supports selector(.todo-list__label__description:has(*)) and (height: 1lh) {
.note-detail-printable .todo-list__label__description {
/* The percentage of the line height that the check box occupies */
--box-ratio: .75;
--box-ratio: 0.75;
/* The size of the gap between the check box and the caption */
--box-text-gap: 0.25em;
@ -303,7 +302,12 @@ pre > code {
orphans: 6;
}
h1, h2, h3, h4, h5, h6 {
h1,
h2,
h3,
h4,
h5,
h6 {
page-break-after: avoid;
break-after: avoid;
}
}

View File

@ -1439,7 +1439,7 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
body.mobile.force-fixed-tree #mobile-rest-container {
flex-direction: column !important;
}
}
body.mobile.force-fixed-tree #detail-container {
flex-grow: 1;

View File

@ -32,7 +32,7 @@
--cmd-button-icon-color: white;
--cmd-button-keyboard-shortcut-background: #0000004d;
--cmd-button-keyboard-shortcut-color: white;
--cmd-button-disabled-opacity: .5;
--cmd-button-disabled-opacity: 0.5;
--icon-button-color: currentColor;
--icon-button-hover-background: var(--hover-item-background-color);
@ -41,7 +41,7 @@
--muted-text-color: #bbb;
--input-background-color: #ffffff12;
--input-text-color: #ffffffc7;
--input-text-color: #ffffffc7;
--input-placeholder-color: #b7b7b782;
--input-selection-background: gray;
--input-selection-text-color: white;

View File

@ -32,7 +32,7 @@
--cmd-button-icon-color: black;
--cmd-button-keyboard-shortcut-background: #00000017;
--cmd-button-keyboard-shortcut-color: black;
--cmd-button-disabled-opacity: .5;
--cmd-button-disabled-opacity: 0.5;
--icon-button-color: currentColor;
--icon-button-hover-background: var(--hover-item-background-color);

View File

@ -61,7 +61,7 @@
--help-backdrop-blur: 10px;
--icon-button-size: 32px;
--icon-button-icon-ratio: .65;
--icon-button-icon-ratio: 0.65;
/* Theme capabilities */
--tab-note-icons: true;
@ -99,4 +99,4 @@ div.tn-tool-dialog {
.note-detail-empty .aa-suggestions div.aa-cursor {
background: var(--hover-item-background-color);
color: var(--hover-item-text-color);
}
}

View File

@ -30,11 +30,11 @@ button.btn.btn-primary:active,
button.btn.btn-secondary:active,
button.btn.btn-sm:not(.select-button):active,
button.btn.btn-success:active {
opacity: .85;
opacity: 0.85;
box-shadow: unset;
background: var(--cmd-button-background-color) !important;
color: var(--cmd-button-text-color) !important;
transform: scale(.95);
transform: scale(0.95);
}
button.btn.btn-primary:disabled,
@ -57,7 +57,7 @@ button.btn.btn-secondary span.bx,
button.btn.btn-sm span.bx,
button.btn.btn-success span.bx {
color: var(--cmd-button-icon-color);
padding-right: .35em;
padding-right: 0.35em;
font-size: 1.2em;
}
@ -66,12 +66,12 @@ button.btn.btn-primary kbd,
button.btn.btn-secondary kbd,
button.btn.btn-sm kbd,
button.btn.btn-success kbd {
margin-left: .5em;
margin-left: 0.5em;
background: var(--cmd-button-keyboard-shortcut-background);
color: var(--cmd-button-keyboard-shortcut-color);
font-size: .6em;
font-size: 0.6em;
text-transform: uppercase;
letter-spacing: .5pt;
letter-spacing: 0.5pt;
}
/*
@ -101,8 +101,8 @@ button.btn.btn-success kbd {
--icon-button-hover-background: var(--tab-close-button-hover-background);
--icon-button-hover-color: var(--tab-close-button-hover-color);
--icon-button-size: 24px;
--icon-button-icon-ratio: .8;
--icon-button-icon-ratio: 0.8;
border-radius: 50%;
}
@ -124,7 +124,7 @@ button.btn.btn-success kbd {
:root .icon-action:not(.global-menu-button):active::before,
:root .tn-tool-button:active::before {
transform: scale(.85);
transform: scale(0.85);
}
:root .icon-action:not(.global-menu-button):focus-visible,
@ -137,7 +137,7 @@ button.btn.btn-success kbd {
*/
input:disabled {
opacity: .33;
opacity: 0.33;
}
/* Text boxes */
@ -190,8 +190,9 @@ textarea:focus,
outline-offset: 0;
background: var(--input-focus-background);
color: var(--input-focus-color);
transition: outline-color 50ms linear,
outline-offset 200ms ease-out;
transition:
outline-color 50ms linear,
outline-offset 200ms ease-out;
}
input::placeholder,
@ -229,12 +230,12 @@ input::selection,
outline: 3px solid var(--input-focus-outline-color);
outline-offset: 0;
background: var(--input-focus-background);
transition: outline-color 50ms linear,
outline-offset 200ms ease-out;
transition:
outline-color 50ms linear,
outline-offset 200ms ease-out;
}
.input-group input
.input-group input:hover,
.input-group input .input-group input:hover,
.input-group input:focus,
.input-group .form-control,
.input-group .form-control:hover,
@ -253,7 +254,7 @@ input::selection,
--accented-background-color: transparent;
background: transparent;
padding: 0 4px;
/* Workaround to set the "color" property. */
--muted-text-color: var(--input-action-button-color);
}
@ -277,7 +278,7 @@ input::selection,
}
.input-group a.disabled {
opacity: .5;
opacity: 0.5;
/* Workaround to set the "background" property. */
--button-disabled-background-color: transparent;
--button-disabled-text-color: var(--input-action-button-color);
@ -290,7 +291,7 @@ input::selection,
.input-group .input-group-text {
/* Background color hijack */
--accented-background-color: transparent;
border: none;
color: var(--input-placeholder-color);
user-select: none;
@ -319,8 +320,7 @@ select.form-control,
outline: 3px solid transparent;
outline-offset: 6px;
padding-right: calc(15px + 1.5rem);
background: var(--input-background-color)
var(--dropdown-arrow);
background: var(--input-background-color) var(--dropdown-arrow);
color: var(--input-text-color);
border: unset;
border-radius: 0.375rem;
@ -330,8 +330,7 @@ select:hover,
select.form-select:hover,
select.form-control:hover,
.select-button.dropdown-toggle.btn:hover {
background: var(--input-hover-background)
var(--dropdown-arrow);
background: var(--input-hover-background) var(--dropdown-arrow);
color: var(--input-hover-color);
}
@ -347,11 +346,11 @@ select.form-control:focus,
box-shadow: unset;
outline: 3px solid var(--input-focus-outline-color);
outline-offset: 0;
background: var(--select-focus-background)
var(--dropdown-arrow);
background: var(--select-focus-background) var(--dropdown-arrow);
color: var(--select-focus-text-color);
transition: outline-color 50ms linear,
outline-offset 200ms ease-out;
transition:
outline-color 50ms linear,
outline-offset 200ms ease-out;
}
option {
@ -360,7 +359,7 @@ option {
optgroup {
color: var(--select-group-heading-text-color);
font-size: .75em;
font-size: 0.75em;
font-weight: normal;
font-style: normal;
line-height: 40px;
@ -374,9 +373,9 @@ optgroup {
* </label>
*/
.tn-file-input {
.tn-file-input {
position: relative;
padding: .375rem 2.25rem .375rem .75rem;
padding: 0.375rem 2.25rem 0.375rem 0.75rem;
}
.tn-file-input input[type="file"] {
@ -416,19 +415,17 @@ optgroup {
/* Check boxes and radio buttons */
@supports selector(label:has(*)) {
/* Check box & radio button commons */
/* The parent label */
label.tn-radio,
label.tn-checkbox {
--box-size: 1em;
--box-label-gap: .5em;
--box-label-gap: 0.5em;
position: relative;
padding-left: calc(var(--box-size) + var(--box-label-gap)) !important;
user-select: none;
}
/* The original input */
@ -455,8 +452,8 @@ optgroup {
height: var(--box-size);
}
label.tn-radio:has(>input[type="radio"]:focus-visible)::before,
label.tn-checkbox:has(>input[type="checkbox"]:focus-visible)::before {
label.tn-radio:has(> input[type="radio"]:focus-visible)::before,
label.tn-checkbox:has(> input[type="checkbox"]:focus-visible)::before {
outline: 2px solid var(--input-focus-outline-color);
}
@ -468,14 +465,15 @@ optgroup {
background: var(--radio-checkbox-background);
}
label.tn-checkbox:has(>input[type="checkbox"]:not(:disabled)):hover:before {
label.tn-checkbox:has(> input[type="checkbox"]:not(:disabled)):hover:before {
background: var(--radio-checkbox-hover-background);
}
@keyframes checkbox-checked {
from {
transform: scale(2);
} to {
}
to {
transform: scale(1);
}
}
@ -484,15 +482,16 @@ optgroup {
label.tn-checkbox::after {
mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3e%3ctitle%3echeck-bold%3c/title%3e%3cpath d='M9%2c20.42L2.79%2c14.21L5.62%2c11.38L9%2c14.77L18.88%2c4.88L21.71%2c7.71L9%2c20.42Z' /%3e%3c/svg%3e");
mask-position: center center;
mask-size: .95em;
mask-size: 0.95em;
background-color: var(--radio-checkbox-indicator-color);
transform: scale(0);
opacity: 0;
transition: transform 300ms ease-out,
opacity 300ms linear;
transition:
transform 300ms ease-out,
opacity 300ms linear;
}
label.tn-checkbox:has(>input[type="checkbox"]:checked)::after {
label.tn-checkbox:has(> input[type="checkbox"]:checked)::after {
opacity: 1;
transform: scale(1);
transition: opacity 100ms ease-in-out;
@ -511,15 +510,16 @@ optgroup {
background: var(--radio-checkbox-background);
}
label.tn-radio:has(>input[type="radio"]:not(:disabled)):hover::before {
label.tn-radio:has(> input[type="radio"]:not(:disabled)):hover::before {
background: var(--radio-checkbox-hover-background);
}
@keyframes radio-checked {
from {
transform: scale(1.5);
} to {
transform: scale(.5);
}
to {
transform: scale(0.5);
}
}
@ -528,12 +528,13 @@ optgroup {
background: var(--radio-checkbox-indicator-color);
transform: scale(0);
opacity: 0;
transition: opacity 300ms linear,
transform 300ms ease-in;
transition:
opacity 300ms linear,
transform 300ms ease-in;
}
label.tn-radio:has(>input[type="radio"]:checked)::after {
transform: scale(.5);
label.tn-radio:has(> input[type="radio"]:checked)::after {
transform: scale(0.5);
opacity: 1;
transition: opacity 150ms linear;
animation: radio-checked 200ms ease-out;
@ -545,9 +546,8 @@ optgroup {
label.tn-radio:has(> input[type="radio"]:disabled)::after,
label.tn-checkbox:has(> input[type="checkbox"]:disabled)::before,
label.tn-checkbox:has(> input[type="checkbox"]:disabled)::after {
opacity: .5;
opacity: 0.5;
}
}
/* Switches */
@ -571,7 +571,7 @@ body a.tn-link:visited,
.use-tn-links a,
.use-tn-links a:visited {
--background: transparent;
box-shadow: 0 0 0 0 var(--background);
border-radius: 4px;
background: var(--background);
@ -579,9 +579,10 @@ body a.tn-link:visited,
font-weight: normal;
text-decoration: underline;
transition: background-color 200ms ease-out,
box-shadow 200ms ease-out,
color 300ms ease-out;
transition:
background-color 200ms ease-out,
box-shadow 200ms ease-out,
color 300ms ease-out;
}
body a.tn-link:focus-visible,
@ -590,15 +591,16 @@ body a.tn-link:focus-visible,
outline-offset: 2px;
}
body a.tn-link:hover,
body a.tn-link:hover,
.use-tn-links a:hover {
box-shadow: 0 0 0 4px var(--link-hover-background);
--background: var(--link-hover-background);
color: var(--link-hover-color);
transition: background-color 100ms ease-in,
box-shadow 100ms ease-in,
color 150ms ease-in;
transition:
background-color 100ms ease-in,
box-shadow 100ms ease-in,
color 150ms ease-in;
}
a.tn-link.external:not(.no-arrow)::after,
@ -606,16 +608,18 @@ a.tn-link[href^="http://"]:not(.no-arrow)::after,
a.tn-link[href^="https://"]:not(.no-arrow)::after,
.use-tn-links a.external:not(.no-arrow)::after,
.use-tn-links a[href^="http://"]:not(.no-arrow)::after,
.use-tn-links a[href^="https://"]:not(.no-arrow)::after {
.use-tn-links a[href^="https://"]:not(.no-arrow)::after
{
display: inline-block;
opacity: .5;
opacity: 0.5;
}
@keyframes link-arrow-blink {
from {
opacity: 1;
} to {
opacity: .5;
}
to {
opacity: 0.5;
}
}
@ -624,7 +628,8 @@ a.tn-link:hover[href^="http://"]:not(.no-arrow)::after,
a.tn-link:hover[href^="https://"]:not(.no-arrow)::after,
.use-tn-links a:hover.external:not(.no-arrow)::after,
.use-tn-links a:hover[href^="http://"]:not(.no-arrow)::after,
.use-tn-links a:hover[href^="https://"]:not(.no-arrow)::after {
.use-tn-links a:hover[href^="https://"]:not(.no-arrow)::after
{
animation: link-arrow-blink 500ms linear alternate infinite;
}
@ -634,4 +639,4 @@ a.tn-link:hover[href^="https://"]:not(.no-arrow)::after,
input[type="range"] {
background: transparent;
}
}

View File

@ -8,4 +8,4 @@ div.note-detail-empty {
.note-detail-empty span.aa-dropdown-menu {
margin-top: 1em;
border: unset;
}
}

View File

@ -152,7 +152,7 @@ div.find-replace-widget div.find-widget-found-wrapper {
font-weight: normal;
}
/* The up / down buttons of the "Find in text" input */
/* The up / down buttons of the "Find in text" input */
.find-replace-widget .input-group button {
font-size: 1.3em;
}
@ -163,4 +163,4 @@ div.find-replace-widget div.find-widget-found-wrapper {
.find-replace-widget .form-check .form-check-input {
margin-left: 0;
}
}

View File

@ -17,7 +17,7 @@ div.note-type-dropdown .check {
margin-right: 6px;
}
/* Editability dropdown */
/* Editability dropdown */
div.editability-dropdown a.dropdown-item {
padding: 4px 16px 4px 0;
@ -29,8 +29,8 @@ div.editability-dropdown a.dropdown-item {
}
.editability-dropdown .dropdown-item .description {
opacity: .75;
font-size: .85em;
opacity: 0.75;
font-size: 0.85em;
}
/*
@ -38,8 +38,8 @@ div.editability-dropdown a.dropdown-item {
*/
.attribute-list .add-new-attribute-button,
.attribute-list .save-attributes-button {
bottom: .3em;
.attribute-list .save-attributes-button {
bottom: 0.3em;
}
.attribute-list .save-attributes-button {
@ -70,12 +70,12 @@ div.editability-dropdown a.dropdown-item {
*/
.note-info-widget-table th {
opacity: .65;
opacity: 0.65;
font-weight: 500;
}
:root .note-info-widget-table button.calculate-button {
min-width: 0;
padding: 4px 10px !important;
font-size: .8em;
}
font-size: 0.8em;
}

View File

@ -85,4 +85,4 @@
.options-section ul.existing-anonymized-databases {
margin: 1em;
}
}

View File

@ -266,7 +266,9 @@ div.quick-search {
padding: var(--padding-top) var(--padding-right) var(--padding-bottom) var(--padding-left);
}
div.quick-search, div.quick-search:hover, div.quick-search:focus-within {
div.quick-search,
div.quick-search:hover,
div.quick-search:focus-within {
/* Prevent changes to background and outline when the state of the input group changes */
background: transparent;
outline: none;
@ -1086,7 +1088,7 @@ html body .dropdown-item[disabled] {
background: transparent;
padding: 1em 8px 14px 8px;
text-transform: uppercase;
font-size: .8em;
font-size: 0.8em;
letter-spacing: 1pt;
color: var(--menu-item-group-header-color) !important;
}
@ -1158,7 +1160,7 @@ body.mobile .dropdown-menu .dropdown-item.submenu-open .dropdown-toggle::after {
}
.calendar-dropdown-widget .calendar-header .calendar-month-selector .select-button {
--select-arrow-svg: ""; /* Disable the dropdown arrow */
--select-arrow-svg: ""; /* Disable the dropdown arrow */
}
@media (max-width: 992px) {
@ -1778,7 +1780,7 @@ div.bookmark-folder-widget .note-link .bx {
.note-title-widget input {
--input-background-color: transparent;
border-radius: 8px;
padding-left: 12px;
}
@ -1789,7 +1791,7 @@ div.bookmark-folder-widget .note-link .bx {
/* Promoted attributes */
.promoted-attribute-cell div.input-group {
margin: 1px 0;
margin: 1px 0;
}
/* Delete notes preview dialog */
@ -1826,13 +1828,13 @@ body.background-effects.zen #root-widget {
--root-background: transparent;
}
/* Alert bar */
@keyframes alert-show {
from {
opacity: 0;
} to {
}
to {
opacity: 1;
}
}
@ -1844,7 +1846,7 @@ body.background-effects.zen #root-widget {
border-radius: 0;
padding: 8px 16px;
background: var(--alert-bar-background) !important;
font-size: .9em;
font-size: 0.9em;
font-weight: normal;
animation: alert-show 300ms ease-in;
border-bottom: 2px solid #0000001c !important;
@ -1866,7 +1868,7 @@ div.promoted-attributes-container {
div.promoted-attributes-container,
div.promoted-attributes-container input {
font-size: .9rem;
font-size: 0.9rem;
}
/* A promoted attribute card */
@ -1905,7 +1907,7 @@ div.promoted-attribute-cell > * {
div.promoted-attribute-cell > label {
font-weight: normal;
white-space: nowrap;
opacity: .75;
opacity: 0.75;
}
div.promoted-attribute-cell:not(:has(input[type="checkbox"])) > label::after {
@ -1955,4 +1957,4 @@ div.promoted-attribute-cell .multiplicity:has(span) {
margin-left: 8px;
margin-right: calc(var(--pa-card-padding-left) - var(--pa-card-padding-right));
font-size: 0; /* Prevent whitespaces creating a gap between buttons */
}
}

View File

@ -8,4 +8,4 @@
:root {
--theme-style-auto: true;
}
}

View File

@ -61,7 +61,7 @@
},
"clone_to": {
"clone_notes_to": "Notizen klonen nach...",
"close":"Schließen",
"close": "Schließen",
"help_on_links": "Hilfe zu Links",
"notes_to_clone": "Notizen zum Klonen",
"target_parent_note": "Ziel-Übergeordnetenotiz",
@ -74,7 +74,7 @@
},
"confirm": {
"confirmation": "Bestätigung",
"close":"Schließen",
"close": "Schließen",
"cancel": "Abbrechen",
"ok": "OK",
"are_you_sure_remove_note": "Bist du sicher, dass du \"{{title}}\" von der Beziehungskarte entfernen möchten? ",
@ -93,7 +93,6 @@
"cancel": "Abbrechen",
"ok": "OK",
"deleted_relation_text": "Notiz {{- note}} (soll gelöscht werden) wird von Beziehung {{- relation}} ausgehend von {{- source}} referenziert."
},
"export": {
"export_note_title": "Notiz exportieren",

View File

@ -43,9 +43,9 @@ function getDayNotesForMonth(req: Request) {
AND attr.value LIKE '${month}%'`;
if (calendarRoot) {
const rows = sql.getRows<{ date: string, noteId: string }>(query);
const rows = sql.getRows<{ date: string; noteId: string }>(query);
const result: Record<string, string> = {};
for (const {date, noteId} of rows) {
for (const { date, noteId } of rows) {
const note = becca.getNote(noteId);
if (note?.hasAncestor(String(calendarRoot))) {
result[date] = noteId;

View File

@ -7,9 +7,7 @@ import yaml from "js-yaml";
import type { JsonObject } from "swagger-ui-express";
const __dirname = dirname(fileURLToPath(import.meta.url));
const etapiDocument = yaml.load(
await readFile(join(__dirname, "../etapi/etapi.openapi.yaml"), "utf8")
) as JsonObject;
const etapiDocument = yaml.load(await readFile(join(__dirname, "../etapi/etapi.openapi.yaml"), "utf8")) as JsonObject;
const apiDocument = JSON.parse(await readFile(join(__dirname, "api", "openapi.json"), "utf-8"));
function register(app: Application) {

View File

@ -1,4 +1,4 @@
import assetPath from "./asset_path.js";
import { isDev } from "./utils.js";
export default isDev ? assetPath + "/app" : assetPath + "/app-dist";
export default isDev ? assetPath + "/app" : assetPath + "/app-dist";

View File

@ -35,7 +35,7 @@ export interface TriliumConfig {
Session: {
cookiePath: string;
cookieMaxAge: number;
}
};
Sync: {
syncServerHost: string;
syncServerTimeout: string;

View File

@ -408,8 +408,7 @@ function checkHiddenSubtreeRecursively(parentNoteId: string, item: HiddenSubtree
value: attr.value,
isInheritable: false
}).save();
} else if (attr.name === "docName"
|| (existingAttribute.noteId.startsWith("_help") && attr.name === "iconClass")) {
} else if (attr.name === "docName" || (existingAttribute.noteId.startsWith("_help") && attr.name === "iconClass")) {
if (existingAttribute.value !== attr.value) {
existingAttribute.value = attr.value ?? "";
console.log("Updating attribute ", attrId);

View File

@ -78,7 +78,7 @@ export default function buildLaunchBarConfig() {
{ id: "_lbProtectedSession", title: t("hidden-subtree.protected-session-title"), type: "launcher", builtinWidget: "protectedSession", icon: "bx bx bx-shield-quarter" },
{ id: "_lbSyncStatus", title: t("hidden-subtree.sync-status-title"), type: "launcher", builtinWidget: "syncStatus", icon: "bx bx-wifi" },
{ id: "_lbSettings", title: t("hidden-subtree.settings-title"), type: "launcher", command: "showOptions", icon: "bx bx-cog" }
]
];
const mobileAvailableLaunchers: HiddenSubtreeItem[] = [
{ id: "_lbMobileNewNote", ...sharedLaunchers.newNote },
@ -98,5 +98,5 @@ export default function buildLaunchBarConfig() {
desktopVisibleLaunchers,
mobileAvailableLaunchers,
mobileVisibleLaunchers
}
};
}

View File

@ -29,7 +29,7 @@ describe("sanitize", () => {
</tbody>
</table>
</figure>`;
const clean = trimIndentation`\
const clean = trimIndentation`\
<p>
<span style="color:hsl(0, 0%, 90%)">
Hi
@ -48,6 +48,6 @@ describe("sanitize", () => {
</tbody>
</table>
</figure>`;
expect(html_sanitizer.sanitize(dirty)) .toBe(clean);
expect(html_sanitizer.sanitize(dirty)).toBe(clean);
});
});

View File

@ -153,20 +153,22 @@ function sanitize(dirtyHtml: string) {
},
allowedStyles: {
"*": {
"color": colorRegex,
color: colorRegex,
"background-color": colorRegex
},
"figure": {
"float": [ /^\s*(left|right|none)\s*$/ ],
"width": sizeRegex,
"height": sizeRegex
figure: {
float: [/^\s*(left|right|none)\s*$/],
width: sizeRegex,
height: sizeRegex
},
"table": {
table: {
"border-color": colorRegex,
"border-style": [ /^\s*(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)\s*$/ ]
"border-style": [/^\s*(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)\s*$/]
},
"td": {
"border": [ /^\s*\d+(?:px|em|%)\s*(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)\s*(#(0x)?[0-9a-fA-F]+|rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)|hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\))\s*$/ ]
td: {
border: [
/^\s*\d+(?:px|em|%)\s*(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)\s*(#(0x)?[0-9a-fA-F]+|rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)|hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\))\s*$/
]
}
},
allowedSchemes: ALLOWED_PROTOCOLS,

View File

@ -3,7 +3,7 @@
import { parse, Renderer, type Tokens } from "marked";
const renderer = new Renderer({ async: false });
renderer.code = ({text, lang, escaped}: Tokens.Code) => {
renderer.code = ({ text, lang, escaped }: Tokens.Code) => {
if (!text) {
return "";
}

View File

@ -20,18 +20,22 @@ async function testImport(fileName: string, mimetype: string) {
codeImportedAsCode: true
});
return new Promise<{ buffer: Buffer, importedNote: BNote }>((resolve, reject) => {
return new Promise<{ buffer: Buffer; importedNote: BNote }>((resolve, reject) => {
cls.init(async () => {
const rootNote = becca.getNote("root");
if (!rootNote) {
reject("Missing root note.");
}
const importedNote = single.importSingleFile(taskContext, {
originalname: fileName,
mimetype,
buffer: buffer
}, rootNote as BNote);
const importedNote = single.importSingleFile(
taskContext,
{
originalname: fileName,
mimetype,
buffer: buffer
},
rootNote as BNote
);
resolve({
buffer,
importedNote
@ -85,4 +89,4 @@ describe("processNoteContent", () => {
expect(importedNote.mime).toBe("text/html");
expect(importedNote.getContent().toString()).toBe("<h2>Hello world</h2>\n<p>Plain text goes here.</p>\n");
});
})
});

View File

@ -61,4 +61,4 @@ describe("processNoteContent", () => {
const htmlNote = rootNote.children.find((ch) => ch.title === "IREN Reports Q2 FY25 Results");
expect(htmlNote?.getContent().toString().substring(0, 4)).toEqual("<div");
});
})
});

View File

@ -3,39 +3,34 @@ import { parseNoteMeta } from "./in_app_help.js";
import type NoteMeta from "./meta/note_meta.js";
describe("In-app help", () => {
it("preserves custom folder icon", () => {
const meta: NoteMeta = {
"isClone": false,
"noteId": "yoAe4jV2yzbd",
"notePath": [
"OkOZllzB3fqN",
"yoAe4jV2yzbd"
],
"title": "Features",
"notePosition": 40,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
isClone: false,
noteId: "yoAe4jV2yzbd",
notePath: ["OkOZllzB3fqN", "yoAe4jV2yzbd"],
title: "Features",
notePosition: 40,
prefix: null,
isExpanded: false,
type: "text",
mime: "text/html",
attributes: [
{
"type": "label",
"name": "iconClass",
"value": "bx bx-star",
"isInheritable": false,
"position": 10
type: "label",
name: "iconClass",
value: "bx bx-star",
isInheritable: false,
position: 10
}
],
"format": "html",
"attachments": [],
"dirFileName": "Features",
"children": []
format: "html",
attachments: [],
dirFileName: "Features",
children: []
};
const item = parseNoteMeta(meta, "/");
const icon = item.attributes?.find((a) => a.name === "iconClass");
expect(icon?.value).toBe("bx bx-star");
});
});

View File

@ -66,8 +66,7 @@ export function parseNoteMeta(noteMeta: NoteMeta, docNameRoot: string): HiddenSu
// Handle text notes
if (noteMeta.type === "text" && noteMeta.dataFileName) {
const docPath = `${docNameRoot}/${path.basename(noteMeta.dataFileName, ".html")}`
.substring(1);
const docPath = `${docNameRoot}/${path.basename(noteMeta.dataFileName, ".html")}`.substring(1);
item.attributes?.push({
type: "label",
name: "docName",
@ -84,7 +83,7 @@ export function parseNoteMeta(noteMeta: NoteMeta, docNameRoot: string): HiddenSu
if (noteMeta.children) {
const children: HiddenSubtreeItem[] = [];
for (const childMeta of noteMeta.children) {
let newDocNameRoot = (noteMeta.dirFileName ? `${docNameRoot}/${noteMeta.dirFileName}` : docNameRoot);
let newDocNameRoot = noteMeta.dirFileName ? `${docNameRoot}/${noteMeta.dirFileName}` : docNameRoot;
children.push(parseNoteMeta(childMeta, newDocNameRoot));
}

View File

@ -734,7 +734,7 @@ function updateNoteData(noteId: string, content: string, attachments: Attachment
note.setContent(newContent, { forceFrontendReload });
if (attachments?.length > 0) {
const existingAttachmentsByTitle = toMap(note.getAttachments({ includeContentLength: false }), "title");
const existingAttachmentsByTitle = toMap(note.getAttachments({ includeContentLength: false }), "title");
for (const { attachmentId, role, mime, title, position, content } of attachments) {
const existingAttachment = existingAttachmentsByTitle.get(title);

Some files were not shown because too many files have changed in this diff Show More