diff --git a/package-lock.json b/package-lock.json index 894c82f64..829b9eece 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@types/js-yaml": "4.0.9", "@types/leaflet": "1.9.16", "@types/react-dom": "18.3.5", - "@types/swagger-ui-express": "4.1.7", + "@types/swagger-ui-express": "4.1.8", "archiver": "7.0.1", "async-mutex": "0.5.0", "autocomplete.js": "0.38.1", @@ -31,6 +31,7 @@ "better-sqlite3": "11.8.1", "bootstrap": "5.3.3", "boxicons": "2.1.4", + "chardet": "2.0.0", "cheerio": "1.0.0", "chokidar": "4.0.3", "cls-hooked": "4.2.2", @@ -97,6 +98,7 @@ "source-map-support": "0.5.21", "split.js": "1.6.5", "stream-throttle": "0.1.3", + "strip-bom": "5.0.0", "striptags": "3.2.0", "swagger-ui-express": "5.0.1", "tmp": "0.2.3", @@ -104,7 +106,7 @@ "turndown": "7.2.0", "unescape": "1.0.1", "vanilla-js-wheel-zoom": "9.0.4", - "ws": "8.18.0", + "ws": "8.18.1", "xml2js": "0.6.2", "yauzl": "3.2.0" }, @@ -143,7 +145,7 @@ "@types/leaflet-gpx": "1.3.7", "@types/mime-types": "2.1.4", "@types/multer": "1.4.12", - "@types/node": "22.13.4", + "@types/node": "22.13.5", "@types/react": "18.3.18", "@types/safe-compare": "1.1.2", "@types/sanitize-html": "2.13.0", @@ -170,7 +172,7 @@ "swagger-jsdoc": "6.2.8", "tslib": "2.8.1", "tsx": "4.19.3", - "typedoc": "0.27.7", + "typedoc": "0.27.8", "typescript": "5.7.3", "vitest": "3.0.6", "webpack": "5.98.0", @@ -4271,9 +4273,9 @@ } }, "node_modules/@types/node": { - "version": "22.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", - "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", + "version": "22.13.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz", + "integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -4432,9 +4434,10 @@ } }, "node_modules/@types/swagger-ui-express": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.7.tgz", - "integrity": "sha512-ovLM9dNincXkzH4YwyYpll75vhzPBlWx6La89wwvYH7mHjVpf0X0K/vR/aUM7SRxmr5tt9z7E5XJcjQ46q+S3g==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz", + "integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==", + "license": "MIT", "dependencies": { "@types/express": "*", "@types/serve-static": "*" @@ -6175,6 +6178,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chardet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.0.0.tgz", + "integrity": "sha512-xVgPpulCooDjY6zH4m9YW3jbkaBe3FKIAvF5sj5t7aBNsVl2ljIE+xwJ4iNgiDZHFQvNIpjdKdVOQvvk5ZfxbQ==", + "license": "MIT" + }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -11889,6 +11898,16 @@ "node": ">=4" } }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -15917,13 +15936,15 @@ } }, "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-5.0.0.tgz", + "integrity": "sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==", "license": "MIT", "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-eof": { @@ -16840,9 +16861,9 @@ } }, "node_modules/typedoc": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.7.tgz", - "integrity": "sha512-K/JaUPX18+61W3VXek1cWC5gwmuLvYTOXJzBvD9W7jFvbPnefRnCHQCEPw7MSNrP/Hj7JJrhZtDDLKdcYm6ucg==", + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.8.tgz", + "integrity": "sha512-q0/2TUunNEDmWkn23ULKGXieK8cgGuAmBUXC/HcZ/rgzMI9Yr4Nq3in1K1vT1NZ9zx6M78yTk3kmIPbwJgK5KA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -18300,9 +18321,9 @@ } }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index e14d45979..7362bd546 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ "server:start-test": "npm run server:switch && rimraf ./data-test && cross-env TRILIUM_DATA_DIR=./data-test TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev TRILIUM_PORT=9999 nodemon src/main.ts", "server:qstart": "npm run server:switch && npm run server:start", "server:switch": "rimraf ./node_modules/better-sqlite3 && npm install", - "electron:start": "cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron ./electron-main.ts --inspect=5858 .", "electron:start-no-dir": "cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_ENV=dev electron --inspect=5858 .", "electron:start-nix": "electron-rebuild --version 33.3.1 && cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./electron-main.ts --inspect=5858 .\"", @@ -37,30 +36,23 @@ "electron:start-prod-nix-no-dir": "electron-rebuild --version 33.3.1 && npm run build:prepare-dist && cross-env TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./dist/electron-main.js --inspect=5858 .\"", "electron:qstart": "npm run electron:switch && npm run electron:start", "electron:switch": "electron-rebuild", - "electron-forge:start": "npm run build:prepare-dist && electron-forge start", "electron-forge:make": "npm run build:prepare-dist && electron-forge make", "electron-forge:package": "npm run build:prepare-dist && electron-forge package", - "docs:build-backend": "rimraf ./docs/backend_api && typedoc ./docs/backend_api src/becca/entities/*.ts src/services/backend_script_api.ts src/services/sql.ts", "docs:build-frontend": "rimraf ./docs/frontend_api && jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js", "docs:build": "npm run docs:build-backend && npm run docs:build-frontend", - "build:webpack": "tsx node_modules/webpack/bin/webpack.js -c webpack.config.ts", "build:prepare-dist": "npm run build:webpack && rimraf ./dist && tsc && tsx ./bin/copy-dist.ts", - "test": "cross-env TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest", "test:coverage": "cross-env TRILIUM_DATA_DIR=./integration-tests/db vitest --coverage", "test:playwright": "playwright test", - "test:integration-edit-db": "cross-env TRILIUM_INTEGRATION_TEST=edit TRILIUM_PORT=8081 TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts", "test:integration-mem-db": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts", "test:integration-mem-db-dev": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts", - "dev:watch-dist": "tsx ./bin/watch-dist.ts", "dev:prettier-check": "prettier . --check", "dev:prettier-fix": "prettier . --write", - "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", @@ -81,7 +73,7 @@ "@types/js-yaml": "4.0.9", "@types/leaflet": "1.9.16", "@types/react-dom": "18.3.5", - "@types/swagger-ui-express": "4.1.7", + "@types/swagger-ui-express": "4.1.8", "archiver": "7.0.1", "async-mutex": "0.5.0", "autocomplete.js": "0.38.1", @@ -89,6 +81,7 @@ "better-sqlite3": "11.8.1", "bootstrap": "5.3.3", "boxicons": "2.1.4", + "chardet": "2.0.0", "cheerio": "1.0.0", "chokidar": "4.0.3", "cls-hooked": "4.2.2", @@ -155,6 +148,7 @@ "source-map-support": "0.5.21", "split.js": "1.6.5", "stream-throttle": "0.1.3", + "strip-bom": "5.0.0", "striptags": "3.2.0", "swagger-ui-express": "5.0.1", "tmp": "0.2.3", @@ -162,7 +156,7 @@ "turndown": "7.2.0", "unescape": "1.0.1", "vanilla-js-wheel-zoom": "9.0.4", - "ws": "8.18.0", + "ws": "8.18.1", "xml2js": "0.6.2", "yauzl": "3.2.0" }, @@ -198,7 +192,7 @@ "@types/leaflet-gpx": "1.3.7", "@types/mime-types": "2.1.4", "@types/multer": "1.4.12", - "@types/node": "22.13.4", + "@types/node": "22.13.5", "@types/react": "18.3.18", "@types/safe-compare": "1.1.2", "@types/sanitize-html": "2.13.0", @@ -225,7 +219,7 @@ "swagger-jsdoc": "6.2.8", "tslib": "2.8.1", "tsx": "4.19.3", - "typedoc": "0.27.7", + "typedoc": "0.27.8", "typescript": "5.7.3", "vitest": "3.0.6", "webpack": "5.98.0", diff --git a/src/public/app/services/note_list_renderer.ts b/src/public/app/services/note_list_renderer.ts index 125e93005..f9a9d0d66 100644 --- a/src/public/app/services/note_list_renderer.ts +++ b/src/public/app/services/note_list_renderer.ts @@ -40,6 +40,10 @@ export default class NoteListRenderer { } } + get isFullHeight() { + return this.viewMode?.isFullHeight; + } + async renderList() { if (!this.viewMode) { return null; diff --git a/src/public/app/services/utils.ts b/src/public/app/services/utils.ts index 6fd5678de..7accea1fb 100644 --- a/src/public/app/services/utils.ts +++ b/src/public/app/services/utils.ts @@ -30,6 +30,27 @@ 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 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; + + 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; +} + function padNum(num: number) { return `${num <= 9 ? "0" : ""}${num}`; } @@ -621,6 +642,7 @@ export default { reloadFrontendApp, reloadTray, parseDate, + getMonthsInDateRange, formatDateISO, formatDateTime, formatTimeInterval, diff --git a/src/public/app/widgets/note_list.ts b/src/public/app/widgets/note_list.ts index 2775aea78..84599d42b 100644 --- a/src/public/app/widgets/note_list.ts +++ b/src/public/app/widgets/note_list.ts @@ -15,6 +15,11 @@ const TPL = ` .note-list-widget .note-list { padding: 10px; } + + .note-list-widget.full-height, + .note-list-widget.full-height .note-list-widget-content { + height: 100%; + }
`; @@ -155,6 +155,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget { .attr("data-attribute-type", valueAttr.type) .attr("data-attribute-name", valueAttr.name) .prop("value", valueAttr.value) + .prop("placeholder", t("promoted_attributes.unset-field-placeholder")) .addClass("form-control") .addClass("promoted-attribute-input") .on("change", (event) => this.promotedAttributeChanged(event)); @@ -226,6 +227,9 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget { } else if (definition.labelType === "boolean") { $input.prop("type", "checkbox"); + $input.wrap($(``)); + $wrapper.find(".input-group").removeClass("input-group"); + if (valueAttr.value === "true") { $input.prop("checked", "checked"); } @@ -272,7 +276,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget { if (definition.multiplicity === "multi") { const $addButton = $("") - .addClass("bx bx-plus pointer") + .addClass("bx bx-plus pointer tn-tool-button") .prop("title", t("promoted_attributes.add_new_attribute")) .on("click", async () => { const $new = await this.createPromotedAttributeCell( @@ -292,7 +296,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget { }); const $removeButton = $("") - .addClass("bx bx-trash pointer") + .addClass("bx bx-trash pointer tn-tool-button") .prop("title", t("promoted_attributes.remove_this_attribute")) .on("click", async () => { const attributeId = $input.attr("data-attribute-id"); diff --git a/src/public/app/widgets/type_widgets/content_widget.ts b/src/public/app/widgets/type_widgets/content_widget.ts index 1f19e2b81..0e9f3ca22 100644 --- a/src/public/app/widgets/type_widgets/content_widget.ts +++ b/src/public/app/widgets/type_widgets/content_widget.ts @@ -14,7 +14,8 @@ import CodeAutoReadOnlySizeOptions from "./options/code_notes/code_auto_read_onl 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.js"; +import PasswordOptions from "./options/password/password.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"; @@ -35,6 +36,7 @@ import RibbonOptions from "./options/appearance/ribbon.js"; import LocalizationOptions from "./options/appearance/i18n.js"; import CodeBlockOptions from "./options/appearance/code_block.js"; import EditorOptions from "./options/text_notes/editor.js"; +import ShareSettingsOptions from "./options/other/share_settings.js"; import type FNote from "../../entities/fnote.js"; import type NoteContextAwareWidget from "../note_context_aware_widget.js"; @@ -64,7 +66,7 @@ const CONTENT_WIDGETS: Record+ + Hi + + + + there + +
++ |
+ + Hi + + + + there + +
++ |
Plain text goes here.
Plain text goes here.
\n"); + }); }) diff --git a/src/services/import/single.ts b/src/services/import/single.ts index 79f90ca2d..b572aea7f 100644 --- a/src/services/import/single.ts +++ b/src/services/import/single.ts @@ -8,7 +8,7 @@ import imageService from "../../services/image.js"; import protectedSessionService from "../protected_session.js"; import markdownService from "./markdown.js"; import mimeService from "./mime.js"; -import { getNoteTitle } from "../../services/utils.js"; +import { getNoteTitle, processStringOrBuffer } from "../../services/utils.js"; import importUtils from "./utils.js"; import htmlSanitizer from "../html_sanitizer.js"; import type { File } from "./common.js"; @@ -69,7 +69,7 @@ function importFile(taskContext: TaskContext, file: File, parentNote: BNote) { function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote) { const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); - const content = file.buffer.toString("utf-8"); + const content = processStringOrBuffer(file.buffer); const detectedMime = mimeService.getMime(file.originalname) || file.mimetype; const mime = mimeService.normalizeMimeType(detectedMime); @@ -89,7 +89,7 @@ function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote) function importPlainText(taskContext: TaskContext, file: File, parentNote: BNote) { const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); - const plainTextContent = file.buffer.toString("utf-8"); + const plainTextContent = processStringOrBuffer(file.buffer); const htmlContent = convertTextToHtml(plainTextContent); const { note } = noteService.createNewNote({ @@ -125,7 +125,7 @@ function convertTextToHtml(text: string) { function importMarkdown(taskContext: TaskContext, file: File, parentNote: BNote) { const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces); - const markdownContent = file.buffer.toString("utf-8"); + const markdownContent = processStringOrBuffer(file.buffer); let htmlContent = markdownService.renderToHtml(markdownContent, title); if (taskContext.data?.safeImport) { @@ -147,7 +147,7 @@ function importMarkdown(taskContext: TaskContext, file: File, parentNote: BNote) } function importHtml(taskContext: TaskContext, file: File, parentNote: BNote) { - let content = file.buffer.toString("utf-8"); + let content = processStringOrBuffer(file.buffer); // Try to get title from HTML first, fall back to filename // We do this before sanitization since that turns all