diff --git a/.prettierignore b/.prettierignore index 42a133ba8..5e1d8fad5 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,5 @@ *.md *.yml libraries/* -docs/* \ No newline at end of file +docs/* +src/public/app/doc_notes/**/* \ No newline at end of file diff --git a/bin/build-server.sh b/bin/build-server.sh index 47bfe9397..ff2912470 100755 --- a/bin/build-server.sh +++ b/bin/build-server.sh @@ -66,8 +66,6 @@ chmod 755 $PKG_DIR/trilium.sh cp bin/tpl/anonymize-database.sql $PKG_DIR/ cp -r translations $PKG_DIR/ -cp -r dump-db $PKG_DIR/ -rm -rf $PKG_DIR/dump-db/node_modules VERSION=`jq -r ".version" package.json` diff --git a/bin/generate-openapi.ts b/bin/generate-openapi.ts index 4bd97a76f..4a2334bc5 100644 --- a/bin/generate-openapi.ts +++ b/bin/generate-openapi.ts @@ -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); diff --git a/e2e/i18n.spec.ts b/e2e/i18n.spec.ts index 70713f097..8c87e08d9 100644 --- a/e2e/i18n.spec.ts +++ b/e2e/i18n.spec.ts @@ -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 }) => { diff --git a/e2e/layout/tab_bar.spec.ts b/e2e/layout/tab_bar.spec.ts index 99391c115..e6df6f328 100644 --- a/e2e/layout/tab_bar.spec.ts +++ b/e2e/layout/tab_bar.spec.ts @@ -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); }); diff --git a/e2e/note_types/code.spec.ts b/e2e/note_types/code.spec.ts index f0f204fcc..29084ff84 100644 --- a/e2e/note_types/code.spec.ts +++ b/e2e/note_types/code.spec.ts @@ -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(); } diff --git a/e2e/note_types/mermaid.spec.ts b/e2e/note_types/mermaid.spec.ts index fd24b4385..f7a0dd401 100644 --- a/e2e/note_types/mermaid.spec.ts +++ b/e2e/note_types/mermaid.spec.ts @@ -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 { diff --git a/e2e/note_types/text.spec.ts b/e2e/note_types/text.spec.ts index 542eb4e33..71e2da2cc 100644 --- a/e2e/note_types/text.spec.ts +++ b/e2e/note_types/text.spec.ts @@ -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"); diff --git a/e2e/support/app.ts b/e2e/support/app.ts index 400af51d2..ff0aa4df7 100644 --- a/e2e/support/app.ts +++ b/e2e/support/app.ts @@ -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(); } - } diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..55ad4d9a2 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,43 @@ +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + eslint.configs.recommended, + tseslint.configs.recommended, + // consider using rules below, once we have a full TS codebase and can be more strict + // tseslint.configs.strictTypeChecked, + // tseslint.configs.stylisticTypeChecked, + // tseslint.configs.recommendedTypeChecked, + { + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname + } + } + }, + { + rules: { + // add rule overrides here + "no-undef": "off", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_", + } + ] + } + }, + { + ignores: [ + "build/*", + "dist/*", + "docs/*", + "libraries/*", + "src/public/app-dist/*", + "src/public/app/doc_notes/*" + ] + } +); diff --git a/package-lock.json b/package-lock.json index 67be79800..1e6c3d38d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,6 @@ "express-session": "1.18.1", "force-graph": "1.49.2", "fs-extra": "11.3.0", - "happy-dom": "17.1.8", "helmet": "8.0.0", "html": "1.0.0", "html2plaintext": "2.1.4", @@ -69,7 +68,6 @@ "jsdom": "26.0.0", "jsplumb": "2.15.6", "katex": "0.16.21", - "knockout": "3.5.1", "leaflet": "1.9.4", "leaflet-gpx": "2.1.2", "mark.js": "8.11.1", @@ -96,7 +94,6 @@ "striptags": "3.2.0", "swagger-ui-express": "5.0.1", "tmp": "0.2.3", - "ts-loader": "9.5.2", "turndown": "7.2.0", "unescape": "1.0.1", "vanilla-js-wheel-zoom": "9.0.4", @@ -117,6 +114,7 @@ "@electron-forge/maker-zip": "7.7.0", "@electron-forge/plugin-auto-unpack-natives": "7.7.0", "@electron/rebuild": "3.7.1", + "@eslint/js": "9.21.0", "@playwright/test": "1.50.1", "@popperjs/core": "2.11.8", "@types/archiver": "6.0.3", @@ -165,14 +163,17 @@ "cross-env": "7.0.3", "css-loader": "7.1.2", "electron": "34.3.0", + "eslint": "9.21.0", "esm": "3.2.25", + "happy-dom": "17.2.2", "i18next-http-backend": "3.0.2", "jsdoc": "4.0.4", + "knockout": "3.5.1", "lorem-ipsum": "2.0.8", "mini-css-extract-plugin": "2.9.2", "nodemon": "3.1.9", "postcss-loader": "8.1.1", - "prettier": "3.5.2", + "prettier": "3.5.3", "rcedit": "4.0.1", "rimraf": "6.0.1", "sass": "1.85.1", @@ -180,10 +181,12 @@ "split.js": "1.6.5", "supertest": "7.0.0", "swagger-jsdoc": "6.2.8", + "ts-loader": "9.5.2", "tslib": "2.8.1", "tsx": "4.19.3", "typedoc": "0.27.9", "typescript": "5.8.2", + "typescript-eslint": "8.26.0", "vitest": "3.0.7", "webpack": "5.98.0", "webpack-cli": "6.0.1", @@ -2127,6 +2130,158 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", + "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz", + "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.12.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@excalidraw/excalidraw": { "version": "0.17.6", "resolved": "https://registry.npmjs.org/@excalidraw/excalidraw/-/excalidraw-0.17.6.tgz", @@ -2202,6 +2357,72 @@ "node": ">=12.0.0" } }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@iconify/types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", @@ -2749,6 +2970,7 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -2763,6 +2985,7 @@ "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2773,6 +2996,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2782,6 +3006,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2791,6 +3016,7 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -2801,6 +3027,7 @@ "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2811,6 +3038,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, "license": "MIT" }, "node_modules/@jsdevtools/ono": { @@ -4366,6 +4594,7 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "*", @@ -4376,6 +4605,7 @@ "version": "3.7.7", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, "license": "MIT", "dependencies": { "@types/eslint": "*", @@ -4386,6 +4616,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, "license": "MIT" }, "node_modules/@types/express": { @@ -4523,6 +4754,7 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, "license": "MIT" }, "node_modules/@types/jsonfile": { @@ -4913,6 +5145,212 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.0.tgz", + "integrity": "sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.26.0", + "@typescript-eslint/type-utils": "8.26.0", + "@typescript-eslint/utils": "8.26.0", + "@typescript-eslint/visitor-keys": "8.26.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.0.tgz", + "integrity": "sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.26.0", + "@typescript-eslint/types": "8.26.0", + "@typescript-eslint/typescript-estree": "8.26.0", + "@typescript-eslint/visitor-keys": "8.26.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.0.tgz", + "integrity": "sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.0", + "@typescript-eslint/visitor-keys": "8.26.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.0.tgz", + "integrity": "sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.26.0", + "@typescript-eslint/utils": "8.26.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.0.tgz", + "integrity": "sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.0.tgz", + "integrity": "sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.0", + "@typescript-eslint/visitor-keys": "8.26.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.0.tgz", + "integrity": "sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.26.0", + "@typescript-eslint/types": "8.26.0", + "@typescript-eslint/typescript-estree": "8.26.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.0.tgz", + "integrity": "sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vitest/coverage-v8": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.7.tgz", @@ -5077,6 +5515,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", @@ -5087,24 +5526,28 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", @@ -5116,12 +5559,14 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -5134,6 +5579,7 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" @@ -5143,6 +5589,7 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" @@ -5152,12 +5599,14 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -5174,6 +5623,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -5187,6 +5637,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -5199,6 +5650,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -5213,6 +5665,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", @@ -5286,12 +5739,14 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, "license": "Apache-2.0" }, "node_modules/abbrev": { @@ -5356,6 +5811,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/agent-base": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", @@ -5396,6 +5861,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -5412,6 +5878,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -5429,6 +5896,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" @@ -6175,6 +6643,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -6187,6 +6656,7 @@ "version": "4.24.3", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6548,6 +7018,7 @@ "version": "1.0.30001689", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001689.tgz", "integrity": "sha512-CmeR2VBycfa+5/jOfnp/NpWPGd06nf1XYiefUvhXFfZE4GkRc9jv+eGPS4nT558WS/8lYCzV8SlANCIPvbWP1g==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6754,6 +7225,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0" @@ -8212,6 +8684,13 @@ "node": ">=4.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -9167,6 +9646,7 @@ "version": "1.5.74", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.74.tgz", "integrity": "sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw==", + "dev": true, "license": "ISC" }, "node_modules/electron-window-state": { @@ -9374,6 +9854,7 @@ "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -9456,6 +9937,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { @@ -9539,6 +10021,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9574,10 +10057,71 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint": { + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz", + "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.2", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.0", + "@eslint/js": "9.21.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -9591,11 +10135,66 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", @@ -9606,10 +10205,42 @@ "node": ">=6" } }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -9622,6 +10253,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -10065,6 +10697,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, "license": "MIT" }, "node_modules/fast-fifo": { @@ -10103,6 +10736,20 @@ "node": ">= 6" } }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -10114,6 +10761,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/fast-xml-parser": { @@ -10167,6 +10815,19 @@ "pend": "~1.2.0" } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/file-type": { "version": "18.7.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz", @@ -10252,6 +10913,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -10320,6 +10982,27 @@ "flat": "cli.js" } }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, "node_modules/float-tooltip": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/float-tooltip/-/float-tooltip-1.7.3.tgz", @@ -10850,10 +11533,24 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/global-agent": { @@ -10900,6 +11597,19 @@ "node": ">=10" } }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -10960,6 +11670,13 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/hachure-fill": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", @@ -10967,9 +11684,10 @@ "license": "MIT" }, "node_modules/happy-dom": { - "version": "17.1.8", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.1.8.tgz", - "integrity": "sha512-Yxbq/FG79z1rhAf/iB6YM8wO2JB/JDQBy99RiLSs+2siEAi5J05x9eW1nnASHZJbpldjJE2KuFLsLZ+AzX/IxA==", + "version": "17.2.2", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.2.2.tgz", + "integrity": "sha512-3I1/CrNi780sdOhuhUnFtgTWhloSc3quSZwsylI41jycx8o97M6Y4aQAu0phSexGusT7+59BxATh4L4xiY0HcA==", + "dev": true, "license": "MIT", "dependencies": { "webidl-conversions": "^7.0.0", @@ -10983,6 +11701,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -11446,6 +12165,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -11818,6 +12547,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -12051,6 +12781,7 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -12065,6 +12796,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -12301,12 +13033,21 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, "license": "MIT" }, "node_modules/json-stringify-safe": { @@ -12439,6 +13180,7 @@ "version": "3.5.1", "resolved": "https://registry.npmjs.org/knockout/-/knockout-3.5.1.tgz", "integrity": "sha512-wRJ9I4az0QcsH7A4v4l0enUpkS++MBx0BnL/68KaLzJg7x1qmbjSlwEoCNol7KTYZ+pmtI7Eh2J0Nu6/2Z5J/Q==", + "dev": true, "license": "MIT" }, "node_modules/kolorist": { @@ -12535,6 +13277,20 @@ "integrity": "sha512-lKoEPlAWel9KXn9keg6Dmyt7gmj5IYyD8CKuxivN+77GpZr2bpKliwFvZJxLUHmNu4fICmCySyxhm5qjZuvvQg==", "license": "BSD-2-Clause" }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", @@ -12605,6 +13361,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.11.5" @@ -12670,6 +13427,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.mergewith": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", @@ -13055,6 +13819,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, "license": "MIT" }, "node_modules/merge2": { @@ -13120,6 +13885,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -13440,6 +14206,13 @@ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "license": "MIT" }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/negotiator": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", @@ -13453,6 +14226,7 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, "license": "MIT" }, "node_modules/ngraph.events": { @@ -13548,6 +14322,7 @@ "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, "license": "MIT" }, "node_modules/nodemon": { @@ -13867,6 +14642,24 @@ "license": "MIT", "peer": true }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -14372,6 +15165,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -14775,10 +15569,20 @@ "node": ">=10" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prettier": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", - "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", "bin": { @@ -15015,6 +15819,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" @@ -15491,6 +16296,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -16038,6 +16844,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", @@ -16190,6 +16997,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" @@ -17167,6 +17975,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -17306,6 +18115,7 @@ "version": "5.37.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -17324,6 +18134,7 @@ "version": "5.3.11", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", @@ -17358,6 +18169,7 @@ "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -17368,6 +18180,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, "license": "MIT" }, "node_modules/test-exclude": { @@ -17597,6 +18410,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -17705,6 +18519,19 @@ "utf8-byte-length": "^1.0.1" } }, + "node_modules/ts-api-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-dedent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", @@ -17718,6 +18545,7 @@ "version": "9.5.2", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", @@ -17738,6 +18566,7 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">= 8" @@ -17805,6 +18634,19 @@ "@mixmark-io/domino": "^2.2.0" } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-fest": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", @@ -17899,6 +18741,7 @@ "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -17908,6 +18751,29 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.26.0.tgz", + "integrity": "sha512-PtVz9nAnuNJuAVeUFvwztjuUgSnJInODAUx47VDwWPXzd5vismPOtPtt83tzNXyOjVQbPRp786D6WFW/M2koIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.26.0", + "@typescript-eslint/parser": "8.26.0", + "@typescript-eslint/utils": "8.26.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -18079,6 +18945,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -18105,6 +18972,16 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/username": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/username/-/username-5.1.0.tgz", @@ -18472,6 +19349,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", @@ -18504,6 +19382,7 @@ "version": "5.98.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", + "dev": true, "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", @@ -18648,6 +19527,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" @@ -18738,7 +19618,6 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } diff --git a/package.json b/package.json index 0588132d8..ba636bc6d 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,8 @@ "dev:watch-dist": "tsx ./bin/watch-dist.ts", "dev:prettier-check": "prettier . --check", "dev:prettier-fix": "prettier . --write", + "dev:linter-check": "eslint .", + "dev:linter-fix": "eslint . --fix", "chore:update-build-info": "tsx bin/update-build-info.ts", "chore:ci-update-nightly-version": "tsx ./bin/update-nightly-version.ts", "chore:generate-document": "cross-env nodemon ./bin/generate_document.ts 1000", @@ -103,7 +105,6 @@ "express-session": "1.18.1", "force-graph": "1.49.2", "fs-extra": "11.3.0", - "happy-dom": "17.1.8", "helmet": "8.0.0", "html": "1.0.0", "html2plaintext": "2.1.4", @@ -123,7 +124,6 @@ "jsdom": "26.0.0", "jsplumb": "2.15.6", "katex": "0.16.21", - "knockout": "3.5.1", "leaflet": "1.9.4", "leaflet-gpx": "2.1.2", "mark.js": "8.11.1", @@ -150,7 +150,6 @@ "striptags": "3.2.0", "swagger-ui-express": "5.0.1", "tmp": "0.2.3", - "ts-loader": "9.5.2", "turndown": "7.2.0", "unescape": "1.0.1", "vanilla-js-wheel-zoom": "9.0.4", @@ -168,6 +167,7 @@ "@electron-forge/maker-zip": "7.7.0", "@electron-forge/plugin-auto-unpack-natives": "7.7.0", "@electron/rebuild": "3.7.1", + "@eslint/js": "9.21.0", "@playwright/test": "1.50.1", "@popperjs/core": "2.11.8", "@types/archiver": "6.0.3", @@ -216,14 +216,17 @@ "cross-env": "7.0.3", "css-loader": "7.1.2", "electron": "34.3.0", + "eslint": "9.21.0", "esm": "3.2.25", + "happy-dom": "17.2.2", "i18next-http-backend": "3.0.2", "jsdoc": "4.0.4", + "knockout": "3.5.1", "lorem-ipsum": "2.0.8", "mini-css-extract-plugin": "2.9.2", "nodemon": "3.1.9", "postcss-loader": "8.1.1", - "prettier": "3.5.2", + "prettier": "3.5.3", "rcedit": "4.0.1", "rimraf": "6.0.1", "sass": "1.85.1", @@ -231,10 +234,12 @@ "split.js": "1.6.5", "supertest": "7.0.0", "swagger-jsdoc": "6.2.8", + "ts-loader": "9.5.2", "tslib": "2.8.1", "tsx": "4.19.3", "typedoc": "0.27.9", "typescript": "5.8.2", + "typescript-eslint": "8.26.0", "vitest": "3.0.7", "webpack": "5.98.0", "webpack-cli": "6.0.1", diff --git a/playwright.config.ts b/playwright.config.ts index e5377db4b..04bc7aaec 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -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 }); diff --git a/spec/etapi/app_info.ts b/spec/etapi/app_info.ts index 08aade29b..9c510d99b 100644 --- a/spec/etapi/app_info.ts +++ b/spec/etapi/app_info.ts @@ -6,4 +6,4 @@ etapi.describeEtapi("app_info", () => { expect(appInfo.clipperProtocolVersion).toEqual("1.0"); }); }); -*/ \ No newline at end of file +*/ diff --git a/spec/etapi/backup.ts b/spec/etapi/backup.ts index 88e487f9e..924213f0e 100644 --- a/spec/etapi/backup.ts +++ b/spec/etapi/backup.ts @@ -7,4 +7,4 @@ etapi.describeEtapi("backup", () => { expect(response.status).toEqual(204); }); }); -*/ \ No newline at end of file +*/ diff --git a/spec/etapi/import.ts b/spec/etapi/import.ts index fc88a9ef5..36782a26a 100644 --- a/spec/etapi/import.ts +++ b/spec/etapi/import.ts @@ -23,4 +23,4 @@ etapi.describeEtapi("import", () => { expect(content).toContain("test export content"); }); }); -*/ \ No newline at end of file +*/ diff --git a/spec/etapi/notes.ts b/spec/etapi/notes.ts index dccb668df..d5c9b680c 100644 --- a/spec/etapi/notes.ts +++ b/spec/etapi/notes.ts @@ -100,4 +100,4 @@ etapi.describeEtapi("notes", () => { expect(error.message).toEqual(`Note '${note.noteId}' not found.`); }); }); -*/ \ No newline at end of file +*/ diff --git a/src/becca/entities/btask.ts b/src/becca/entities/btask.ts index 6f2b89373..fd0ddfdd6 100644 --- a/src/becca/entities/btask.ts +++ b/src/becca/entities/btask.ts @@ -14,7 +14,7 @@ export default class BTask extends AbstractBeccaEntity { } static get hashedProperties() { - return [ "taskId", "parentNoteId", "title", "dueDate", "isDone", "isDeleted" ]; + return ["taskId", "parentNoteId", "title", "dueDate", "isDone", "isDeleted"]; } taskId?: string; diff --git a/src/public/app/components/app_context.ts b/src/public/app/components/app_context.ts index 5f05862b4..bafeaaa60 100644 --- a/src/public/app/components/app_context.ts +++ b/src/public/app/components/app_context.ts @@ -24,6 +24,7 @@ import type NoteTreeWidget from "../widgets/note_tree.js"; import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js"; import type { ContextMenuEvent } from "../menus/context_menu.js"; import type TypeWidget from "../widgets/type_widgets/type_widget.js"; +import type EditableTextTypeWidget from "../widgets/type_widgets/editable_text.js"; interface Layout { getRootWidget: (appContext: AppContext) => RootWidget; @@ -62,7 +63,7 @@ export interface NoteCommandData extends CommandData { } export interface ExecuteCommandData extends CommandData { - resolve: (data: T) => void + resolve: (data: T) => void; } /** @@ -70,7 +71,7 @@ export interface ExecuteCommandData extends CommandData { */ export type CommandMappings = { "api-log-messages": CommandData; - focusTree: CommandData, + focusTree: CommandData; focusOnTitle: CommandData; focusOnDetail: CommandData; focusOnSearchDefinition: Required; @@ -108,7 +109,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,8 +132,10 @@ export type CommandMappings = { editNoteTitle: ContextMenuCommandData; protectSubtree: ContextMenuCommandData; unprotectSubtree: ContextMenuCommandData; - openBulkActionsDialog: ContextMenuCommandData | { - selectedOrActiveNoteIds?: string[] + openBulkActionsDialog: + | ContextMenuCommandData + | { + selectedOrActiveNoteIds?: string[]; }; editBranchPrefix: ContextMenuCommandData; convertNoteToAttachment: ContextMenuCommandData; @@ -221,11 +224,11 @@ export type CommandMappings = { moveTabToNewWindow: CommandData; copyTabToNewWindow: CommandData; closeActiveTab: CommandData & { - $el: JQuery - }, + $el: JQuery; + }; setZoomFactorAndSave: { zoomFactor: string; - } + }; reEvaluateRightPaneVisibility: CommandData; runActiveNote: CommandData; @@ -234,18 +237,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 +351,7 @@ type EventMappings = { ntxId: string | null | undefined; // TODO: deduplicate ntxId }; tabReorder: { - ntxIdsInOrder: string[] + ntxIdsInOrder: string[]; }; refreshNoteList: { noteId: string; @@ -359,6 +362,12 @@ type EventMappings = { relationMapResetPanZoom: { ntxId: string | null | undefined }; relationMapResetZoomIn: { ntxId: string | null | undefined }; relationMapResetZoomOut: { ntxId: string | null | undefined }; + activeNoteChangedEvent: {}; + showAddLinkDialog: { + textTypeWidget: EditableTextTypeWidget; + text: string; + }; + }; export type EventListener = { diff --git a/src/public/app/components/component.ts b/src/public/app/components/component.ts index 416449fc7..e2897d1f3 100644 --- a/src/public/app/components/component.ts +++ b/src/public/app/components/component.ts @@ -18,7 +18,7 @@ export class TypedComponent> { children: ChildT[]; initialized: Promise | null; parent?: TypedComponent; - position!: number; + _position!: number; constructor() { this.componentId = `${this.sanitizedClassName}-${utils.randomString(8)}`; @@ -31,6 +31,14 @@ export class TypedComponent> { return this.constructor.name.replace(/[^A-Z0-9]/gi, "_"); } + get position() { + return this._position; + } + + set position(newPosition: number) { + this._position = newPosition; + } + setParent(parent: TypedComponent) { this.parent = parent; return this; diff --git a/src/public/app/components/shortcut_component.ts b/src/public/app/components/shortcut_component.ts index 11c20342a..de2bded97 100644 --- a/src/public/app/components/shortcut_component.ts +++ b/src/public/app/components/shortcut_component.ts @@ -17,7 +17,7 @@ export default class ShortcutComponent extends Component implements EventListene } bindNoteShortcutHandler(labelOrRow: AttributeRow) { - const handler = () => appContext.tabManager.getActiveContext().setNote(labelOrRow.noteId); + const handler = () => appContext.tabManager.getActiveContext()?.setNote(labelOrRow.noteId); const namespace = labelOrRow.attributeId; if (labelOrRow.isDeleted) { diff --git a/src/public/app/components/tab_manager.js b/src/public/app/components/tab_manager.js index 7a7b2f2cf..46a0f9d96 100644 --- a/src/public/app/components/tab_manager.js +++ b/src/public/app/components/tab_manager.js @@ -248,7 +248,7 @@ export default class TabManager extends Component { await noteContext.setEmpty(); } - async openEmptyTab(ntxId = null, hoistedNoteId = "root", mainNtxId = null) { + async openEmptyTab(ntxId = null, hoistedNoteId = "root", mainNtxId) { const noteContext = new NoteContext(ntxId, hoistedNoteId, mainNtxId); const existingNoteContext = this.children.find((nc) => nc.ntxId === noteContext.ntxId); diff --git a/src/public/app/entities/fnote.ts b/src/public/app/entities/fnote.ts index eea5a5cc4..95ad748fd 100644 --- a/src/public/app/entities/fnote.ts +++ b/src/public/app/entities/fnote.ts @@ -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; diff --git a/src/public/app/layouts/desktop_layout.js b/src/public/app/layouts/desktop_layout.js index 1ece1c156..ecb97fe5f 100644 --- a/src/public/app/layouts/desktop_layout.js +++ b/src/public/app/layouts/desktop_layout.js @@ -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) { diff --git a/src/public/app/login.ts b/src/public/app/login.ts index ed37e9e95..5fc7d6293 100644 --- a/src/public/app/login.ts +++ b/src/public/app/login.ts @@ -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) -} \ No newline at end of file + 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); +} diff --git a/src/public/app/menus/launcher_context_menu.ts b/src/public/app/menus/launcher_context_menu.ts index 3aa436632..2ac7decee 100644 --- a/src/public/app/menus/launcher_context_menu.ts +++ b/src/public/app/menus/launcher_context_menu.ts @@ -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(); diff --git a/src/public/app/menus/tree_context_menu.ts b/src/public/app/menus/tree_context_menu.ts index 5c2c74fe9..4cc094e0b 100644 --- a/src/public/app/menus/tree_context_menu.ts +++ b/src/public/app/menus/tree_context_menu.ts @@ -44,7 +44,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener { 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 }))); diff --git a/src/public/app/services/attribute_renderer.ts b/src/public/app/services/attribute_renderer.ts index caecf1602..bbe278056 100644 --- a/src/public/app/services/attribute_renderer.ts +++ b/src/public/app/services/attribute_renderer.ts @@ -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(); diff --git a/src/public/app/services/branches.ts b/src/public/app/services/branches.ts index eab70ff19..18bae3529 100644 --- a/src/public/app/services/branches.ts +++ b/src/public/app/services/branches.ts @@ -152,10 +152,10 @@ async function deleteNotes(branchIdsToDelete: string[], forceDeleteAllClones = f async function activateParentNotePath() { // this is not perfect, maybe we should find the next/previous sibling, but that's more complex const activeContext = appContext.tabManager.getActiveContext(); - const parentNotePathArr = activeContext.notePathArray.slice(0, -1); + const parentNotePathArr = activeContext?.notePathArray.slice(0, -1); - if (parentNotePathArr.length > 0) { - activeContext.setNote(parentNotePathArr.join("/")); + if (parentNotePathArr && parentNotePathArr.length > 0) { + activeContext?.setNote(parentNotePathArr.join("/")); } } diff --git a/src/public/app/services/frontend_script_api.ts b/src/public/app/services/frontend_script_api.ts index 86a793c9f..bd30a58cc 100644 --- a/src/public/app/services/frontend_script_api.ts +++ b/src/public/app/services/frontend_script_api.ts @@ -457,13 +457,13 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig this.BasicWidget = BasicWidget; this.activateNote = async (notePath) => { - await appContext.tabManager.getActiveContext().setNote(notePath); + await appContext.tabManager.getActiveContext()?.setNote(notePath); }; this.activateNewNote = async (notePath) => { await ws.waitForMaxKnownEntityChangeId(); - await appContext.tabManager.getActiveContext().setNote(notePath); + await appContext.tabManager.getActiveContext()?.setNote(notePath); await appContext.triggerEvent("focusAndSelectTitle", {}); }; @@ -480,8 +480,8 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig this.openSplitWithNote = async (notePath, activate) => { await ws.waitForMaxKnownEntityChangeId(); - const subContexts = appContext.tabManager.getActiveContext().getSubContexts(); - const { ntxId } = subContexts[subContexts.length - 1]; + const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts(); + const { ntxId } = subContexts?.[subContexts.length - 1] ?? {}; await appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath }); @@ -591,15 +591,48 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig this.addTextToActiveContextEditor = (text) => appContext.triggerCommand("addTextToActiveEditor", { text }); - this.getActiveContextNote = () => appContext.tabManager.getActiveContextNote(); - this.getActiveContext = () => appContext.tabManager.getActiveContext(); - this.getActiveMainContext = () => appContext.tabManager.getActiveMainContext(); + this.getActiveContextNote = (): FNote => { + const note = appContext.tabManager.getActiveContextNote(); + if (!note) { + throw new Error("No active context note found"); + } + return note; + }; + + this.getActiveContext = (): NoteContext => { + const context = appContext.tabManager.getActiveContext(); + if (!context) { + throw new Error("No active context found"); + } + return context; + }; + + this.getActiveMainContext = (): NoteContext => { + const context = appContext.tabManager.getActiveMainContext(); + if (!context) { + throw new Error("No active main context found"); + } + return context; + }; this.getNoteContexts = () => appContext.tabManager.getNoteContexts(); this.getMainNoteContexts = () => appContext.tabManager.getMainNoteContexts(); - this.getActiveContextTextEditor = () => appContext.tabManager.getActiveContext()?.getTextEditor(); - this.getActiveContextCodeEditor = () => appContext.tabManager.getActiveContext()?.getCodeEditor(); + this.getActiveContextTextEditor = () => { + const context = appContext.tabManager.getActiveContext(); + if (!context) { + throw new Error("No active context found"); + } + return context.getTextEditor(); + }; + + this.getActiveContextCodeEditor = () => { + const context = appContext.tabManager.getActiveContext(); + if (!context) { + throw new Error("No active context found"); + } + return context.getCodeEditor(); + }; this.getActiveNoteDetailWidget = () => new Promise((resolve) => appContext.triggerCommand("executeInActiveNoteDetailWidget", { callback: resolve })); this.getActiveContextNotePath = () => appContext.tabManager.getActiveContextNotePath(); @@ -665,5 +698,5 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig } export default FrontendScriptApi as any as { - new (startNote: FNote, currentNote: FNote, originEntity: Entity | null, $container: JQuery | null): Api; + new(startNote: FNote, currentNote: FNote, originEntity: Entity | null, $container: JQuery | null): Api; }; diff --git a/src/public/app/services/import.ts b/src/public/app/services/import.ts index 97cc40c94..0c6c25f11 100644 --- a/src/public/app/services/import.ts +++ b/src/public/app/services/import.ts @@ -80,7 +80,7 @@ ws.subscribeToMessages(async (message) => { toastService.showPersistent(toast); if (message.result.importedNoteId) { - await appContext.tabManager.getActiveContext().setNote(message.result.importedNoteId); + await appContext.tabManager.getActiveContext()?.setNote(message.result.importedNoteId); } } }); @@ -102,7 +102,7 @@ ws.subscribeToMessages(async (message) => { toastService.showPersistent(toast); if (message.result.parentNoteId) { - await appContext.tabManager.getActiveContext().setNote(message.result.importedNoteId, { + await appContext.tabManager.getActiveContext()?.setNote(message.result.importedNoteId, { viewScope: { viewMode: "attachments" } diff --git a/src/public/app/services/library_loader.ts b/src/public/app/services/library_loader.ts index 829184155..85bc1082c 100644 --- a/src/public/app/services/library_loader.ts +++ b/src/public/app/services/library_loader.ts @@ -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) { diff --git a/src/public/app/services/link.ts b/src/public/app/services/link.ts index 9b3c779bb..8cda26fa5 100644 --- a/src/public/app/services/link.ts +++ b/src/public/app/services/link.ts @@ -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) { diff --git a/src/public/app/services/note_autocomplete.ts b/src/public/app/services/note_autocomplete.ts index 7a6cc7eb3..18311733b 100644 --- a/src/public/app/services/note_autocomplete.ts +++ b/src/public/app/services/note_autocomplete.ts @@ -322,9 +322,7 @@ function init() { $.fn.setSelectedNotePath = function (notePath) { notePath = notePath || ""; - $(this).attr(SELECTED_NOTE_PATH_KEY, notePath); - $(this).closest(".input-group").find(".go-to-selected-note-button").toggleClass("disabled", !notePath.trim()).attr("href", `#${notePath}`); // we also set href here so tooltip can be displayed }; @@ -336,11 +334,9 @@ function init() { } }; - $.fn.setSelectedExternalLink = function (externalLink) { - if (externalLink) { - // TODO: This doesn't seem to do anything with the external link, is it normal? - $(this).closest(".input-group").find(".go-to-selected-note-button").toggleClass("disabled", true); - } + $.fn.setSelectedExternalLink = function (externalLink: string | null) { + $(this).attr(SELECTED_EXTERNAL_LINK_KEY, externalLink); + $(this).closest(".input-group").find(".go-to-selected-note-button").toggleClass("disabled", true); }; $.fn.setNote = async function (noteId) { diff --git a/src/public/app/services/note_create.ts b/src/public/app/services/note_create.ts index 90dd94d8f..47cf02bb6 100644 --- a/src/public/app/services/note_create.ts +++ b/src/public/app/services/note_create.ts @@ -86,8 +86,8 @@ async function createNote(parentNotePath: string | undefined, options: CreateNot await ws.waitForMaxKnownEntityChangeId(); - if (options.activate) { - const activeNoteContext = appContext.tabManager.getActiveContext(); + const activeNoteContext = appContext.tabManager.getActiveContext(); + if (activeNoteContext && options.activate) { await activeNoteContext.setNote(`${parentNotePath}/${note.noteId}`); if (options.focus === "title") { @@ -152,8 +152,7 @@ async function duplicateSubtree(noteId: string, parentNotePath: string) { await ws.waitForMaxKnownEntityChangeId(); - const activeNoteContext = appContext.tabManager.getActiveContext(); - activeNoteContext.setNote(`${parentNotePath}/${note.noteId}`); + appContext.tabManager.getActiveContext()?.setNote(`${parentNotePath}/${note.noteId}`); const origNote = await froca.getNote(noteId); toastService.showMessage(t("note_create.duplicated", { title: origNote?.title })); diff --git a/src/public/app/services/note_list_renderer.ts b/src/public/app/services/note_list_renderer.ts index f9a9d0d66..ede0c3132 100644 --- a/src/public/app/services/note_list_renderer.ts +++ b/src/public/app/services/note_list_renderer.ts @@ -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); diff --git a/src/public/app/services/note_tooltip.ts b/src/public/app/services/note_tooltip.ts index 28937f3e2..19ac71801 100644 --- a/src/public/app/services/note_tooltip.ts +++ b/src/public/app/services/note_tooltip.ts @@ -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"); } diff --git a/src/public/app/services/utils.ts b/src/public/app/services/utils.ts index 5a407c0cd..40233a425 100644 --- a/src/public/app/services/utils.ts +++ b/src/public/app/services/utils.ts @@ -36,20 +36,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; @@ -164,7 +164,7 @@ function escapeHtml(str: string) { } export function escapeQuotes(value: string) { - return value.replaceAll("\"", """); + return value.replaceAll('"', """); } function formatSize(size: number) { diff --git a/src/public/app/share.ts b/src/public/app/share.ts index 62c9467e7..8f5ebebad 100644 --- a/src/public/app/share.ts +++ b/src/public/app/share.ts @@ -32,4 +32,4 @@ document.addEventListener( // add fetchNote as property to the window object Object.defineProperty(window, "fetchNote", { value: fetchNote -}); \ No newline at end of file +}); diff --git a/src/public/app/types-fancytree.d.ts b/src/public/app/types-fancytree.d.ts index c016dad8a..38a4b0295 100644 --- a/src/public/app/types-fancytree.d.ts +++ b/src/public/app/types-fancytree.d.ts @@ -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. */ diff --git a/src/public/app/types-lib.d.ts b/src/public/app/types-lib.d.ts index d84101b80..51155abfb 100644 --- a/src/public/app/types-lib.d.ts +++ b/src/public/app/types-lib.d.ts @@ -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; } diff --git a/src/public/app/types.d.ts b/src/public/app/types.d.ts index 6e05c9fb1..470617923 100644 --- a/src/public/app/types.d.ts +++ b/src/public/app/types.d.ts @@ -85,7 +85,7 @@ declare global { getSelectedNotePath(): string | undefined; getSelectedNoteId(): string | null; setSelectedNotePath(notePath: string | null | undefined); - getSelectedExternalLink(this: HTMLElement): string | undefined; + getSelectedExternalLink(): string | undefined; setSelectedExternalLink(externalLink: string | null | undefined); setNote(noteId: string); markRegExp(regex: RegExp, opts: { diff --git a/src/public/app/widgets/bookmark_buttons.ts b/src/public/app/widgets/bookmark_buttons.ts index 72b422540..b32393c9a 100644 --- a/src/public/app/widgets/bookmark_buttons.ts +++ b/src/public/app/widgets/bookmark_buttons.ts @@ -41,7 +41,7 @@ export default class BookmarkButtons extends FlexContainer { : new OpenNoteButtonWidget(note).class("launcher-button"); if (this.settings.titlePlacement) { - if (!('settings' in buttonWidget)) { + if (!("settings" in buttonWidget)) { (buttonWidget as any).settings = {}; } diff --git a/src/public/app/widgets/buttons/attachments_actions.ts b/src/public/app/widgets/buttons/attachments_actions.ts index efacf48fe..e7b82b28f 100644 --- a/src/public/app/widgets/buttons/attachments_actions.ts +++ b/src/public/app/widgets/buttons/attachments_actions.ts @@ -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($('').attr("title", t("attachments_actions.open_custom_client_only"))); } - } async openAttachmentCommand() { @@ -170,11 +168,10 @@ export default class AttachmentActionsWidget extends BasicWidget { return; } - const { note: newNote } = await server.post>(`attachments/${this.attachmentId}/convert-to-note`); toastService.showMessage(t("attachments_actions.convert_success", { title: this.attachment.title })); await ws.waitForMaxKnownEntityChangeId(); - await appContext.tabManager.getActiveContext().setNote(newNote.noteId); + await appContext.tabManager.getActiveContext()?.setNote(newNote.noteId); } async renameAttachmentCommand() { diff --git a/src/public/app/widgets/buttons/calendar.ts b/src/public/app/widgets/buttons/calendar.ts index b279e7689..131d176bf 100644 --- a/src/public/app/widgets/buttons/calendar.ts +++ b/src/public/app/widgets/buttons/calendar.ts @@ -43,8 +43,8 @@ const DROPDOWN_TPL = ` data-calendar-input="month"> @@ -149,7 +149,7 @@ export default class CalendarWidget extends RightDropdownButtonWidget { const note = await dateNoteService.getDayNote(date); if (note) { - appContext.tabManager.getActiveContext().setNote(note.noteId); + appContext.tabManager.getActiveContext()?.setNote(note.noteId); this.dropdown?.hide(); } else { toastService.showError(t("calendar.cannot_find_day_note")); @@ -189,10 +189,7 @@ export default class CalendarWidget extends RightDropdownButtonWidget { async dropdownShown() { await libraryLoader.requireLibrary(libraryLoader.CALENDAR_WIDGET); - - const activeNote = appContext.tabManager.getActiveContextNote(); - - this.init(activeNote?.getOwnedLabelValue("dateNote")); + this.init(appContext.tabManager.getActiveContextNote()?.getOwnedLabelValue("dateNote") ?? null); } init(activeDate: string | null) { diff --git a/src/public/app/widgets/buttons/launcher/note_launcher.ts b/src/public/app/widgets/buttons/launcher/note_launcher.ts index 950021091..7622df401 100644 --- a/src/public/app/widgets/buttons/launcher/note_launcher.ts +++ b/src/public/app/widgets/buttons/launcher/note_launcher.ts @@ -78,7 +78,7 @@ export default class NoteLauncher extends AbstractLauncher { } getHoistedNoteId() { - return this.launcherNote.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext().hoistedNoteId; + return this.launcherNote.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext()?.hoistedNoteId; } getTitle() { diff --git a/src/public/app/widgets/buttons/launcher/today_launcher.ts b/src/public/app/widgets/buttons/launcher/today_launcher.ts index 838fa41c2..7e203bb7b 100644 --- a/src/public/app/widgets/buttons/launcher/today_launcher.ts +++ b/src/public/app/widgets/buttons/launcher/today_launcher.ts @@ -10,6 +10,6 @@ export default class TodayLauncher extends NoteLauncher { } getHoistedNoteId() { - return appContext.tabManager.getActiveContext().hoistedNoteId; + return appContext.tabManager.getActiveContext()?.hoistedNoteId; } } diff --git a/src/public/app/widgets/buttons/note_actions.ts b/src/public/app/widgets/buttons/note_actions.ts index dc332b541..a1ecd45d9 100644 --- a/src/public/app/widgets/buttons/note_actions.ts +++ b/src/public/app/widgets/buttons/note_actions.ts @@ -228,7 +228,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget { toastService.showMessage(t("note_actions.convert_into_attachment_successful", { title: newAttachment.title })); await ws.waitForMaxKnownEntityChangeId(); - await appContext.tabManager.getActiveContext().setNote(newAttachment.ownerId, { + await appContext.tabManager.getActiveContext()?.setNote(newAttachment.ownerId, { viewScope: { viewMode: "attachments", attachmentId: newAttachment.attachmentId diff --git a/src/public/app/widgets/containers/left_pane_container.ts b/src/public/app/widgets/containers/left_pane_container.ts index bbfedaa2a..21d4b280e 100644 --- a/src/public/app/widgets/containers/left_pane_container.ts +++ b/src/public/app/widgets/containers/left_pane_container.ts @@ -24,8 +24,7 @@ export default class LeftPaneContainer extends FlexContainer { if (visible) { this.triggerEvent("focusTree", {}); } else { - const activeNoteContext = appContext.tabManager.getActiveContext(); - this.triggerEvent("focusOnDetail", { ntxId: activeNoteContext.ntxId }); + this.triggerEvent("focusOnDetail", { ntxId: appContext.tabManager.getActiveContext()?.ntxId }); } } } diff --git a/src/public/app/widgets/containers/ribbon_container.ts b/src/public/app/widgets/containers/ribbon_container.ts index b3ed7fd35..e357919db 100644 --- a/src/public/app/widgets/containers/ribbon_container.ts +++ b/src/public/app/widgets/containers/ribbon_container.ts @@ -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 = $('
') .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($('').addClass(ret.icon).attr("title", ret.title).attr("data-toggle-command", (ribbonWidget as any).toggleCommand)) // TODO: base class + .append( + $('') + .addClass(ret.icon) + .attr("title", ret.title) + .attr("data-toggle-command", (ribbonWidget as any).toggleCommand) + ) // TODO: base class .append(" ") .append($('').text(ret.title)); diff --git a/src/public/app/widgets/containers/split_note_container.js b/src/public/app/widgets/containers/split_note_container.ts similarity index 65% rename from src/public/app/widgets/containers/split_note_container.js rename to src/public/app/widgets/containers/split_note_container.ts index 28f71369e..61f82b793 100644 --- a/src/public/app/widgets/containers/split_note_container.js +++ b/src/public/app/widgets/containers/split_note_container.ts @@ -1,8 +1,29 @@ import FlexContainer from "./flex_container.js"; import appContext from "../../components/app_context.js"; +import NoteContext from "../../components/note_context.js"; +import type { CommandMappings, EventNames, EventData } from "../../components/app_context.js"; +import type BasicWidget from "../basic_widget.js"; -export default class SplitNoteContainer extends FlexContainer { - constructor(widgetFactory) { +interface NoteContextEvent { + noteContext: NoteContext; +} + +interface SplitNoteWidget extends BasicWidget { + hasBeenAlreadyShown?: boolean; + ntxId?: string; +} + +type WidgetFactory = () => SplitNoteWidget; + +interface Widgets { + [key: string]: SplitNoteWidget; +} + +export default class SplitNoteContainer extends FlexContainer { + private widgetFactory: WidgetFactory; + private widgets: Widgets; + + constructor(widgetFactory: WidgetFactory) { super("row"); this.widgetFactory = widgetFactory; @@ -13,7 +34,7 @@ export default class SplitNoteContainer extends FlexContainer { this.collapsible(); } - async newNoteContextCreatedEvent({ noteContext }) { + async newNoteContextCreatedEvent({ noteContext }: NoteContextEvent) { const widget = this.widgetFactory(); const $renderedWidget = widget.render(); @@ -23,20 +44,32 @@ export default class SplitNoteContainer extends FlexContainer { this.$widget.append($renderedWidget); - widget.handleEvent("initialRenderComplete"); + widget.handleEvent("initialRenderComplete", {}); widget.toggleExt(false); - this.widgets[noteContext.ntxId] = widget; + if (noteContext.ntxId) { + this.widgets[noteContext.ntxId] = widget; + } await widget.handleEvent("setNoteContext", { noteContext }); this.child(widget); } - async openNewNoteSplitEvent({ ntxId, notePath, hoistedNoteId, viewScope }) { + async openNewNoteSplitEvent({ ntxId, notePath, hoistedNoteId, viewScope }: { + ntxId: string; + notePath?: string; + hoistedNoteId?: string; + viewScope?: any; + }) { const mainNtxId = appContext.tabManager.getActiveMainContext().ntxId; + if (!mainNtxId) { + logError("empty mainNtxId!"); + return; + } + if (!ntxId) { logError("empty ntxId!"); @@ -53,7 +86,7 @@ export default class SplitNoteContainer extends FlexContainer { // insert the note context after the originating note context ntxIds.splice(ntxIds.indexOf(ntxId) + 1, 0, noteContext.ntxId); - this.triggerCommand("noteContextReorder", { ntxIdsInOrder: ntxIds }); + this.triggerCommand("noteContextReorder" as keyof CommandMappings, { ntxIdsInOrder: ntxIds }); // move the note context rendered widget after the originating widget this.$widget.find(`[data-ntx-id="${noteContext.ntxId}"]`).insertAfter(this.$widget.find(`[data-ntx-id="${ntxId}"]`)); @@ -67,11 +100,11 @@ export default class SplitNoteContainer extends FlexContainer { } } - closeThisNoteSplitCommand({ ntxId }) { + closeThisNoteSplitCommand({ ntxId }: { ntxId: string }): void { appContext.tabManager.removeNoteContext(ntxId); } - async moveThisNoteSplitCommand({ ntxId, isMovingLeft }) { + async moveThisNoteSplitCommand({ ntxId, isMovingLeft }: { ntxId: string; isMovingLeft: boolean }): Promise { if (!ntxId) { logError("empty ntxId!"); return; @@ -96,7 +129,7 @@ export default class SplitNoteContainer extends FlexContainer { const newNtxIds = [...ntxIds.slice(0, leftIndex), ntxIds[leftIndex + 1], ntxIds[leftIndex], ...ntxIds.slice(leftIndex + 2)]; const isChangingMainContext = !contexts[leftIndex].mainNtxId; - this.triggerCommand("noteContextReorder", { + this.triggerCommand("noteContextReorder" as keyof CommandMappings, { ntxIdsInOrder: newNtxIds, oldMainNtxId: isChangingMainContext ? ntxIds[leftIndex] : null, newMainNtxId: isChangingMainContext ? ntxIds[leftIndex + 1] : null @@ -109,16 +142,16 @@ export default class SplitNoteContainer extends FlexContainer { await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]); } - activeContextChangedEvent() { + activeContextChangedEvent(): void { this.refresh(); } - noteSwitchedAndActivatedEvent() { + noteSwitchedAndActivatedEvent(): void { this.refresh(); } - noteContextRemovedEvent({ ntxIds }) { - this.children = this.children.filter((c) => !ntxIds.includes(c.ntxId)); + noteContextRemovedEvent({ ntxIds }: { ntxIds: string[] }): void { + this.children = this.children.filter((c) => c.ntxId && !ntxIds.includes(c.ntxId)); for (const ntxId of ntxIds) { this.$widget.find(`[data-ntx-id="${ntxId}"]`).remove(); @@ -127,7 +160,7 @@ export default class SplitNoteContainer extends FlexContainer { } } - contextsReopenedEvent({ ntxId, afterNtxId }) { + contextsReopenedEvent({ ntxId, afterNtxId }: { ntxId?: string; afterNtxId?: string }): void { if (ntxId === undefined || afterNtxId === undefined) { // no single split reopened return; @@ -135,13 +168,11 @@ export default class SplitNoteContainer extends FlexContainer { this.$widget.find(`[data-ntx-id="${ntxId}"]`).insertAfter(this.$widget.find(`[data-ntx-id="${afterNtxId}"]`)); } - async refresh() { + async refresh(): Promise { this.toggleExt(true); } - toggleInt(show) {} // not needed - - toggleExt(show) { + toggleExt(show: boolean): void { const activeMainContext = appContext.tabManager.getActiveMainContext(); const activeNtxId = activeMainContext ? activeMainContext.ntxId : null; @@ -149,7 +180,7 @@ export default class SplitNoteContainer extends FlexContainer { const noteContext = appContext.tabManager.getNoteContextById(ntxId); const widget = this.widgets[ntxId]; - widget.toggleExt(show && activeNtxId && [noteContext.ntxId, noteContext.mainNtxId].includes(activeNtxId)); + widget.toggleExt(show && activeNtxId !== null && [noteContext.ntxId, noteContext.mainNtxId].includes(activeNtxId)); } } @@ -158,41 +189,50 @@ export default class SplitNoteContainer extends FlexContainer { * are not executed, we're waiting for the first tab activation, and then we update the tab. After this initial * activation, further note switches are always propagated to the tabs. */ - handleEventInChildren(name, data) { + handleEventInChildren(name: T, data: EventData): Promise | null { if (["noteSwitched", "noteSwitchedAndActivated"].includes(name)) { // this event is propagated only to the widgets of a particular tab - const widget = this.widgets[data.noteContext.ntxId]; + const noteContext = (data as NoteContextEvent).noteContext; + const widget = noteContext.ntxId ? this.widgets[noteContext.ntxId] : undefined; if (!widget) { return Promise.resolve(); } - if (widget.hasBeenAlreadyShown || name === "noteSwitchedAndActivated" || appContext.tabManager.getActiveMainContext() === data.noteContext.getMainContext()) { + if (widget.hasBeenAlreadyShown || name === "noteSwitchedAndActivatedEvent" || appContext.tabManager.getActiveMainContext() === noteContext.getMainContext()) { widget.hasBeenAlreadyShown = true; - return [widget.handleEvent("noteSwitched", data), this.refreshNotShown(data)]; + return Promise.all([ + widget.handleEvent("noteSwitched", { noteContext, notePath: noteContext.notePath }), + this.refreshNotShown({ noteContext }) + ]); } else { return Promise.resolve(); } } if (name === "activeContextChanged") { - return this.refreshNotShown(data); + return this.refreshNotShown(data as NoteContextEvent); } else { return super.handleEventInChildren(name, data); } } - refreshNotShown(data) { - const promises = []; + private refreshNotShown(data: NoteContextEvent): Promise { + const promises: Promise[] = []; for (const subContext of data.noteContext.getMainContext().getSubContexts()) { + if (!subContext.ntxId) { + continue; + } + const widget = this.widgets[subContext.ntxId]; if (!widget.hasBeenAlreadyShown) { widget.hasBeenAlreadyShown = true; - promises.push(widget.handleEvent("activeContextChanged", { noteContext: subContext })); + const eventPromise = widget.handleEvent("activeContextChanged", { noteContext: subContext }); + promises.push(eventPromise || Promise.resolve()); } } diff --git a/src/public/app/widgets/dialogs/about.js b/src/public/app/widgets/dialogs/about.ts similarity index 79% rename from src/public/app/widgets/dialogs/about.js rename to src/public/app/widgets/dialogs/about.ts index 918e725af..790e5e578 100644 --- a/src/public/app/widgets/dialogs/about.js +++ b/src/public/app/widgets/dialogs/about.ts @@ -5,6 +5,15 @@ import openService from "../../services/open.js"; import server from "../../services/server.js"; import utils from "../../services/utils.js"; +interface AppInfo { + appVersion: string; + dbVersion: number; + syncVersion: number; + buildDate: string; + buildRevision: string; + dataDirectory: string; +} + const TPL = ` `; 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: '© OpenStreetMap 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: '© OpenStreetMap contributors', + detectRetina: true + }).addTo(map); + }); } } diff --git a/src/public/app/widgets/mermaid.ts b/src/public/app/widgets/mermaid.ts index 417400c03..8fcd3efdf 100644 --- a/src/public/app/widgets/mermaid.ts +++ b/src/public/app/widgets/mermaid.ts @@ -66,7 +66,7 @@ export default class MermaidWidget extends NoteContextAwareWidget { mermaid.mermaidAPI.initialize({ startOnLoad: false, - ...getMermaidConfig() as any + ...(getMermaidConfig() as any) }); this.$display.empty(); diff --git a/src/public/app/widgets/mobile_widgets/mobile_detail_menu.ts b/src/public/app/widgets/mobile_widgets/mobile_detail_menu.ts index 7d4fc3ddd..58039f910 100644 --- a/src/public/app/widgets/mobile_widgets/mobile_detail_menu.ts +++ b/src/public/app/widgets/mobile_widgets/mobile_detail_menu.ts @@ -28,8 +28,8 @@ class MobileDetailMenuWidget extends BasicWidget { x: e.pageX, y: e.pageY, items: [ - { title: t("mobile_detail_menu.insert_child_note"), command: "insertChildNote", uiIcon: "bx bx-plus", enabled: note.type !== "search" }, - { title: t("mobile_detail_menu.delete_this_note"), command: "delete", uiIcon: "bx bx-trash", enabled: note.noteId !== "root" } + { title: t("mobile_detail_menu.insert_child_note"), command: "insertChildNote", uiIcon: "bx bx-plus", enabled: note?.type !== "search" }, + { title: t("mobile_detail_menu.delete_this_note"), command: "delete", uiIcon: "bx bx-trash", enabled: note?.noteId !== "root" } ], selectMenuItemHandler: async ({ command }) => { if (command === "insertChildNote") { diff --git a/src/public/app/widgets/note_detail.ts b/src/public/app/widgets/note_detail.ts index 9995d2197..1fd5411e3 100644 --- a/src/public/app/widgets/note_detail.ts +++ b/src/public/app/widgets/note_detail.ts @@ -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 | "empty" | "readOnlyCode" | "readOnlyText" | "editableText" | "editableCode" | "attachmentDetail" | "attachmentList" | "protectedSession"; +type ExtendedNoteType = + | Exclude + | "empty" + | "readOnlyCode" + | "readOnlyText" + | "editableText" + | "editableCode" + | "attachmentDetail" + | "attachmentList" + | "protectedSession"; export default class NoteDetailWidget extends NoteContextAwareWidget { @@ -332,7 +341,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)); diff --git a/src/public/app/widgets/note_map.ts b/src/public/app/widgets/note_map.ts index c6cb53ea7..45cefa1d9 100644 --- a/src/public/app/widgets/note_map.ts +++ b/src/public/app/widgets/note_map.ts @@ -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; } @@ -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(); }) @@ -324,7 +322,7 @@ export default class NoteMapWidget extends NoteContextAwareWidget { .warmupTicks(30) .onNodeClick((node) => { if (node.id) { - appContext.tabManager.getActiveContext().setNote((node as Node).id); + appContext.tabManager.getActiveContext()?.setNote((node as Node).id); } }) .onNodeRightClick((node, e) => { @@ -373,7 +371,7 @@ export default class NoteMapWidget extends NoteContextAwareWidget { if (mapRootNoteId === "hoisted") { mapRootNoteId = hoistedNoteService.getHoistedNoteId(); } else if (!mapRootNoteId) { - mapRootNoteId = appContext.tabManager.getActiveContext().parentNoteId; + mapRootNoteId = appContext.tabManager.getActiveContext()?.parentNoteId; } return mapRootNoteId ?? ""; @@ -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; diff --git a/src/public/app/widgets/note_title.ts b/src/public/app/widgets/note_title.ts index 56ff57472..690c18685 100644 --- a/src/public/app/widgets/note_title.ts +++ b/src/public/app/widgets/note_title.ts @@ -94,7 +94,7 @@ export default class NoteTitleWidget extends NoteContextAwareWidget { || 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); diff --git a/src/public/app/widgets/note_tree.ts b/src/public/app/widgets/note_tree.ts index 93016edf8..d1ec85611 100644 --- a/src/public/app/widgets/note_tree.ts +++ b/src/public/app/widgets/note_tree.ts @@ -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; private $treeActions!: JQuery; private $treeSettingsButton!: JQuery; @@ -425,10 +424,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { const activeNoteContext = appContext.tabManager.getActiveContext(); const opts: SetNoteOpts = {}; - if (activeNoteContext.viewScope?.viewMode === "contextual-help") { + if (activeNoteContext?.viewScope?.viewMode === "contextual-help") { opts.viewScope = activeNoteContext.viewScope; } - await activeNoteContext.setNote(notePath, opts); + await activeNoteContext?.setNote(notePath, opts); }, expand: (event, data) => this.setExpanded(data.node.data.branchId, true), collapse: (event, data) => this.setExpanded(data.node.data.branchId, false), @@ -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) { @@ -617,10 +619,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { // TODO: Deduplicate with server's notes.ts#getAndValidateParent if (!["search", "launcher"].includes(note.type) - && !note.isOptions() - && !note.isLaunchBarConfig() - && !note.noteId.startsWith("_help") - ) { + && !note.isOptions() + && !note.isLaunchBarConfig() + && !note.noteId.startsWith("_help") + ) { const $createChildNoteButton = $(``).on( "click", cancelClickPropagation @@ -1756,6 +1758,6 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { await ws.waitForMaxKnownEntityChangeId(); - appContext.tabManager.getActiveContext().setNote(resp.note.noteId); + appContext.tabManager.getActiveContext()?.setNote(resp.note.noteId); } } diff --git a/src/public/app/widgets/protected_note_switch.ts b/src/public/app/widgets/protected_note_switch.ts index e4926ae84..a65f4db31 100644 --- a/src/public/app/widgets/protected_note_switch.ts +++ b/src/public/app/widgets/protected_note_switch.ts @@ -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"); diff --git a/src/public/app/widgets/ribbon_widgets/book_properties.ts b/src/public/app/widgets/ribbon_widgets/book_properties.ts index 1a0671e3a..1acb565d1 100644 --- a/src/public/app/widgets/ribbon_widgets/book_properties.ts +++ b/src/public/app/widgets/ribbon_widgets/book_properties.ts @@ -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 })); } diff --git a/src/public/app/widgets/ribbon_widgets/note_map.ts b/src/public/app/widgets/ribbon_widgets/note_map.ts index 911636b2d..94a467879 100644 --- a/src/public/app/widgets/ribbon_widgets/note_map.ts +++ b/src/public/app/widgets/ribbon_widgets/note_map.ts @@ -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) diff --git a/src/public/app/widgets/ribbon_widgets/note_paths.ts b/src/public/app/widgets/ribbon_widgets/note_paths.ts index 1bb95cf73..986758992 100644 --- a/src/public/app/widgets/ribbon_widgets/note_paths.ts +++ b/src/public/app/widgets/ribbon_widgets/note_paths.ts @@ -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(); } } diff --git a/src/public/app/widgets/ribbon_widgets/note_properties.ts b/src/public/app/widgets/ribbon_widgets/note_properties.ts index bfccfe51f..4be3654dd 100644 --- a/src/public/app/widgets/ribbon_widgets/note_properties.ts +++ b/src/public/app/widgets/ribbon_widgets/note_properties.ts @@ -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 ?? ""); } } diff --git a/src/public/app/widgets/ribbon_widgets/search_definition.ts b/src/public/app/widgets/ribbon_widgets/search_definition.ts index 2328f996f..08df15426 100644 --- a/src/public/app/widgets/ribbon_widgets/search_definition.ts +++ b/src/public/app/widgets/ribbon_widgets/search_definition.ts @@ -246,7 +246,7 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget { await ws.waitForMaxKnownEntityChangeId(); - await appContext.tabManager.getActiveContext().setNote(notePath); + await appContext.tabManager.getActiveContext()?.setNote(notePath); // Note the {{- notePathTitle}} in json file is not typo, it's unescaping // See https://www.i18next.com/translation-function/interpolation#unescape toastService.showMessage(t("search_definition.search_note_saved", { notePathTitle: await treeService.getNotePathTitle(notePath) })); diff --git a/src/public/app/widgets/ribbon_widgets/similar_notes.ts b/src/public/app/widgets/ribbon_widgets/similar_notes.ts index f169b559d..c35469471 100644 --- a/src/public/app/widgets/ribbon_widgets/similar_notes.ts +++ b/src/public/app/widgets/ribbon_widgets/similar_notes.ts @@ -40,7 +40,6 @@ interface SimilarNote { noteId: string; } - export default class SimilarNotesWidget extends NoteContextAwareWidget { private $similarNotesWrapper!: JQuery; diff --git a/src/public/app/widgets/switch.ts b/src/public/app/widgets/switch.ts index aff5587c9..63fd23fce 100644 --- a/src/public/app/widgets/switch.ts +++ b/src/public/app/widgets/switch.ts @@ -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) { diff --git a/src/public/app/widgets/sync_status.ts b/src/public/app/widgets/sync_status.ts index f5dc186b6..dc413c410 100644 --- a/src/public/app/widgets/sync_status.ts +++ b/src/public/app/widgets/sync_status.ts @@ -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, diff --git a/src/public/app/widgets/tab_row.ts b/src/public/app/widgets/tab_row.ts index 9f122784b..f0af54493 100644 --- a/src/public/app/widgets/tab_row.ts +++ b/src/public/app/widgets/tab_row.ts @@ -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; diff --git a/src/public/app/widgets/template_switch.ts b/src/public/app/widgets/template_switch.ts index 572426020..50c11f657 100644 --- a/src/public/app/widgets/template_switch.ts +++ b/src/public/app/widgets/template_switch.ts @@ -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(); } diff --git a/src/public/app/widgets/toc.ts b/src/public/app/widgets/toc.ts index 6a843a11f..db15ba6a8 100644 --- a/src/public/app/widgets/toc.ts +++ b/src/public/app/widgets/toc.ts @@ -55,8 +55,8 @@ const TPL = `
`; interface Toc { - $toc: JQuery, - headingCount: number + $toc: JQuery; + 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; diff --git a/src/public/app/widgets/type_widgets/attachment_detail.ts b/src/public/app/widgets/type_widgets/attachment_detail.ts index 5fc8e4201..a89954d23 100644 --- a/src/public/app/widgets/type_widgets/attachment_detail.ts +++ b/src/public/app/widgets/type_widgets/attachment_detail.ts @@ -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("" + t("attachment_detail.attachment_deleted") + ""); diff --git a/src/public/app/widgets/type_widgets/attachment_list.ts b/src/public/app/widgets/type_widgets/attachment_list.ts index 3018f784b..f39f7782c 100644 --- a/src/public/app/widgets/type_widgets/attachment_list.ts +++ b/src/public/app/widgets/type_widgets/attachment_list.ts @@ -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 diff --git a/src/public/app/widgets/type_widgets/book.ts b/src/public/app/widgets/type_widgets/book.ts index 38be2a56c..e708483f4 100644 --- a/src/public/app/widgets/type_widgets/book.ts +++ b/src/public/app/widgets/type_widgets/book.ts @@ -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">) { diff --git a/src/public/app/widgets/type_widgets/canvas.ts b/src/public/app/widgets/type_widgets/canvas.ts index 4ba76fa9d..c8b822552 100644 --- a/src/public/app/widgets/type_widgets/canvas.ts +++ b/src/public/app/widgets/type_widgets/canvas.ts @@ -59,9 +59,9 @@ const TPL = ` `; interface CanvasContent { - elements: ExcalidrawElement[], - files: BinaryFileData[], - appState: Partial + elements: ExcalidrawElement[]; + files: BinaryFileData[]; + appState: Partial; } 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(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 }); @@ -541,10 +541,6 @@ export default class ExcalidrawTypeWidget extends TypeWidget { excalidrawAPI: (api: ExcalidrawImperativeAPI) => { this.excalidrawApi = api; }, - onPaste: (data: unknown, event: unknown) => { - console.log("Verbose: excalidraw internal paste. No trilium action implemented.", data, event); - return false; - }, onLibraryChange: () => { this.libraryChanged = true; diff --git a/src/public/app/widgets/type_widgets/ckeditor/toolbars.ts b/src/public/app/widgets/type_widgets/ckeditor/toolbars.ts index 425fa819c..3f7ef3e49 100644 --- a/src/public/app/widgets/type_widgets/ckeditor/toolbars.ts +++ b/src/public/app/widgets/type_widgets/ckeditor/toolbars.ts @@ -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" + ] }; } diff --git a/src/public/app/widgets/type_widgets/content_widget.ts b/src/public/app/widgets/type_widgets/content_widget.ts index ceb958883..b3408d129 100644 --- a/src/public/app/widgets/type_widgets/content_widget.ts +++ b/src/public/app/widgets/type_widgets/content_widget.ts @@ -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"; diff --git a/src/public/app/widgets/type_widgets/editable_text.js b/src/public/app/widgets/type_widgets/editable_text.js index 67c622de2..44ea52e92 100644 --- a/src/public/app/widgets/type_widgets/editable_text.js +++ b/src/public/app/widgets/type_widgets/editable_text.js @@ -313,7 +313,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { this.watchdog?.editor.editing.view.focus(); } - show() {} + show() { } getEditor() { return this.watchdog?.editor; @@ -360,14 +360,14 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { this.addTextToEditor(text); } - async addLink(notePath, linkTitle) { + async addLink(notePath, linkTitle, externalLink = false) { await this.initialized; if (linkTitle) { if (this.hasSelection()) { - this.watchdog.editor.execute("link", `#${notePath}`); + this.watchdog.editor.execute("link", externalLink ? `${notePath}` : `#${notePath}`); } else { - await this.addLinkToEditor(`#${notePath}`, linkTitle); + await this.addLinkToEditor(externalLink ? `${notePath}` : `#${notePath}`, linkTitle); } } else { this.watchdog.editor.execute("referenceLink", { href: "#" + notePath }); diff --git a/src/public/app/widgets/type_widgets/geo_map.ts b/src/public/app/widgets/type_widgets/geo_map.ts index 32f689e6f..dddccbac8 100644 --- a/src/public/app/widgets/type_widgets/geo_map.ts +++ b/src/public/app/widgets/type_widgets/geo_map.ts @@ -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 { ${title}`, - 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(); } } diff --git a/src/public/app/widgets/type_widgets/mind_map.ts b/src/public/app/widgets/type_widgets/mind_map.ts index afa34df06..02fa6a1ff 100644 --- a/src/public/app/widgets/type_widgets/mind_map.ts +++ b/src/public/app/widgets/type_widgets/mind_map.ts @@ -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(); } diff --git a/src/public/app/widgets/type_widgets/options/appearance/code_block.ts b/src/public/app/widgets/type_widgets/options/appearance/code_block.ts index 7e42a59d6..4dcebb20e 100644 --- a/src/public/app/widgets/type_widgets/options/appearance/code_block.ts +++ b/src/public/app/widgets/type_widgets/options/appearance/code_block.ts @@ -60,7 +60,7 @@ interface Theme { val: string; } -type Response = Record +type Response = Record; /** * Contains appearance settings for code blocks within text notes, such as the theme for the syntax highlighter. diff --git a/src/public/app/widgets/type_widgets/options/appearance/electron_integration.ts b/src/public/app/widgets/type_widgets/options/appearance/electron_integration.ts index 884347630..803d3d8ea 100644 --- a/src/public/app/widgets/type_widgets/options/appearance/electron_integration.ts +++ b/src/public/app/widgets/type_widgets/options/appearance/electron_integration.ts @@ -32,7 +32,7 @@ const TPL = `
- + `; diff --git a/src/public/app/widgets/type_widgets/options/appearance/theme.ts b/src/public/app/widgets/type_widgets/options/appearance/theme.ts index 52b23355a..a50e74586 100644 --- a/src/public/app/widgets/type_widgets/options/appearance/theme.ts +++ b/src/public/app/widgets/type_widgets/options/appearance/theme.ts @@ -94,10 +94,12 @@ export default class ThemeOptions extends OptionsWidget { this.$themeSelect.empty(); for (const theme of themes) { - this.$themeSelect.append($("