mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 11:02:28 +08:00
Merge remote-tracking branch 'origin/develop' into feature/task_list
This commit is contained in:
commit
0e5b8af3a4
831
package-lock.json
generated
831
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@ -49,7 +49,7 @@
|
|||||||
"build:webpack": "tsx node_modules/webpack/bin/webpack.js -c webpack.config.ts",
|
"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",
|
"build:prepare-dist": "npm run build:webpack && rimraf ./dist && tsc && tsx ./bin/copy-dist.ts",
|
||||||
|
|
||||||
"test": "cross-env TRILIUM_DATA_DIR=./integration-tests/db vitest",
|
"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:coverage": "cross-env TRILIUM_DATA_DIR=./integration-tests/db vitest --coverage",
|
||||||
"test:playwright": "playwright test",
|
"test:playwright": "playwright test",
|
||||||
|
|
||||||
@ -167,14 +167,14 @@
|
|||||||
"yauzl": "3.2.0"
|
"yauzl": "3.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@electron-forge/cli": "7.6.1",
|
"@electron-forge/cli": "7.7.0",
|
||||||
"@electron-forge/maker-deb": "7.6.1",
|
"@electron-forge/maker-deb": "7.7.0",
|
||||||
"@electron-forge/maker-dmg": "7.6.1",
|
"@electron-forge/maker-dmg": "7.7.0",
|
||||||
"@electron-forge/maker-flatpak": "7.6.1",
|
"@electron-forge/maker-flatpak": "7.7.0",
|
||||||
"@electron-forge/maker-rpm": "7.6.1",
|
"@electron-forge/maker-rpm": "7.7.0",
|
||||||
"@electron-forge/maker-squirrel": "7.6.1",
|
"@electron-forge/maker-squirrel": "7.7.0",
|
||||||
"@electron-forge/maker-zip": "7.6.1",
|
"@electron-forge/maker-zip": "7.7.0",
|
||||||
"@electron-forge/plugin-auto-unpack-natives": "7.6.1",
|
"@electron-forge/plugin-auto-unpack-natives": "7.7.0",
|
||||||
"@electron/rebuild": "3.7.1",
|
"@electron/rebuild": "3.7.1",
|
||||||
"@playwright/test": "1.50.1",
|
"@playwright/test": "1.50.1",
|
||||||
"@types/archiver": "6.0.3",
|
"@types/archiver": "6.0.3",
|
||||||
@ -212,7 +212,7 @@
|
|||||||
"@types/ws": "8.5.14",
|
"@types/ws": "8.5.14",
|
||||||
"@types/xml2js": "0.4.14",
|
"@types/xml2js": "0.4.14",
|
||||||
"@types/yargs": "17.0.33",
|
"@types/yargs": "17.0.33",
|
||||||
"@vitest/coverage-v8": "3.0.5",
|
"@vitest/coverage-v8": "3.0.6",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"electron": "34.2.0",
|
"electron": "34.2.0",
|
||||||
"esm": "3.2.25",
|
"esm": "3.2.25",
|
||||||
@ -224,10 +224,10 @@
|
|||||||
"rimraf": "6.0.1",
|
"rimraf": "6.0.1",
|
||||||
"swagger-jsdoc": "6.2.8",
|
"swagger-jsdoc": "6.2.8",
|
||||||
"tslib": "2.8.1",
|
"tslib": "2.8.1",
|
||||||
"tsx": "4.19.2",
|
"tsx": "4.19.3",
|
||||||
"typedoc": "0.27.7",
|
"typedoc": "0.27.7",
|
||||||
"typescript": "5.7.3",
|
"typescript": "5.7.3",
|
||||||
"vitest": "3.0.5",
|
"vitest": "3.0.6",
|
||||||
"webpack": "5.98.0",
|
"webpack": "5.98.0",
|
||||||
"webpack-cli": "6.0.1",
|
"webpack-cli": "6.0.1",
|
||||||
"webpack-dev-middleware": "7.4.2"
|
"webpack-dev-middleware": "7.4.2"
|
||||||
|
@ -222,7 +222,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
|||||||
|
|
||||||
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 L = this.L;
|
||||||
const icon = this.#buildIcon(note.getIcon(), note.title);
|
const icon = this.#buildIcon(note.getIcon(), note.getColorClass(), note.title);
|
||||||
|
|
||||||
const marker = L.marker(L.latLng(lat, lng), {
|
const marker = L.marker(L.latLng(lat, lng), {
|
||||||
icon,
|
icon,
|
||||||
@ -257,12 +257,12 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
|||||||
this.currentMarkerData[note.noteId] = marker;
|
this.currentMarkerData[note.noteId] = marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
#buildIcon(bxIconClass: string, title: string) {
|
#buildIcon(bxIconClass: string, colorClass: string, title: string) {
|
||||||
return this.L.divIcon({
|
return this.L.divIcon({
|
||||||
html: `\
|
html: `\
|
||||||
<img class="icon" src="${asset_path}/node_modules/leaflet/dist/images/marker-icon.png" />
|
<img class="icon" src="${asset_path}/node_modules/leaflet/dist/images/marker-icon.png" />
|
||||||
<img class="icon-shadow" src="${asset_path}/node_modules/leaflet/dist/images/marker-shadow.png" />
|
<img class="icon-shadow" src="${asset_path}/node_modules/leaflet/dist/images/marker-shadow.png" />
|
||||||
<span class="bx ${bxIconClass}"></span>
|
<span class="bx ${bxIconClass} ${colorClass}"></span>
|
||||||
<span class="title-label">${title}</span>`,
|
<span class="title-label">${title}</span>`,
|
||||||
iconSize: [ 25, 41 ],
|
iconSize: [ 25, 41 ],
|
||||||
iconAnchor: [ 12, 41 ]
|
iconAnchor: [ 12, 41 ]
|
||||||
@ -361,7 +361,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
|||||||
// If any of note has its location attribute changed.
|
// If any of note has its location attribute changed.
|
||||||
// TODO: Should probably filter by parent here as well.
|
// TODO: Should probably filter by parent here as well.
|
||||||
const attributeRows = loadResults.getAttributeRows();
|
const attributeRows = loadResults.getAttributeRows();
|
||||||
if (attributeRows.find((at) => at.name === LOCATION_ATTRIBUTE)) {
|
if (attributeRows.find((at) => [ LOCATION_ATTRIBUTE, "color" ].includes(at.name ?? ""))) {
|
||||||
this.#reloadMarkers();
|
this.#reloadMarkers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import OptionsWidget from "./options_widget.js";
|
|||||||
import toastService from "../../../services/toast.js";
|
import toastService from "../../../services/toast.js";
|
||||||
import { t } from "../../../services/i18n.js";
|
import { t } from "../../../services/i18n.js";
|
||||||
import type { OptionDefinitions, OptionMap } from "../../../../../services/options_interface.js";
|
import type { OptionDefinitions, OptionMap } from "../../../../../services/options_interface.js";
|
||||||
|
import optionsService from "../../../services/options.js";
|
||||||
|
|
||||||
type TimeSelectorConstructor = {
|
type TimeSelectorConstructor = {
|
||||||
widgetId: string;
|
widgetId: string;
|
||||||
@ -9,6 +10,7 @@ type TimeSelectorConstructor = {
|
|||||||
optionValueId: keyof OptionDefinitions;
|
optionValueId: keyof OptionDefinitions;
|
||||||
optionTimeScaleId: keyof OptionDefinitions;
|
optionTimeScaleId: keyof OptionDefinitions;
|
||||||
includedTimeScales?: Set<TimeSelectorScale>;
|
includedTimeScales?: Set<TimeSelectorScale>;
|
||||||
|
minimumSeconds?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TimeSelectorScale = "seconds" | "minutes" | "hours" | "days";
|
type TimeSelectorScale = "seconds" | "minutes" | "hours" | "days";
|
||||||
@ -43,6 +45,7 @@ export default class TimeSelector extends OptionsWidget {
|
|||||||
private optionValueId: keyof OptionDefinitions;
|
private optionValueId: keyof OptionDefinitions;
|
||||||
private optionTimeScaleId: keyof OptionDefinitions;
|
private optionTimeScaleId: keyof OptionDefinitions;
|
||||||
private includedTimeScales: Set<TimeSelectorScale>;
|
private includedTimeScales: Set<TimeSelectorScale>;
|
||||||
|
private minimumSeconds: number;
|
||||||
|
|
||||||
constructor(options: TimeSelectorConstructor) {
|
constructor(options: TimeSelectorConstructor) {
|
||||||
super();
|
super();
|
||||||
@ -50,7 +53,8 @@ export default class TimeSelector extends OptionsWidget {
|
|||||||
this.widgetLabelId = options.widgetLabelId;
|
this.widgetLabelId = options.widgetLabelId;
|
||||||
this.optionValueId = options.optionValueId;
|
this.optionValueId = options.optionValueId;
|
||||||
this.optionTimeScaleId = options.optionTimeScaleId;
|
this.optionTimeScaleId = options.optionTimeScaleId;
|
||||||
this.includedTimeScales = !options.includedTimeScales ? new Set(["seconds", "minutes", "hours", "days"]) : options.includedTimeScales;
|
this.includedTimeScales = options.includedTimeScales || new Set(["seconds", "minutes", "hours", "days"]);
|
||||||
|
this.minimumSeconds = options.minimumSeconds || 0
|
||||||
}
|
}
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
@ -71,7 +75,8 @@ export default class TimeSelector extends OptionsWidget {
|
|||||||
|
|
||||||
if (!this.handleTimeValidation() || typeof timeScale !== "string" || !time) return;
|
if (!this.handleTimeValidation() || typeof timeScale !== "string" || !time) return;
|
||||||
|
|
||||||
this.internalTimeInSeconds = this.convertTime(time, timeScale).toOption();
|
this.setInternalTimeInSeconds(this.convertTime(time, timeScale).toOption());
|
||||||
|
|
||||||
this.updateOption(this.optionValueId, this.internalTimeInSeconds);
|
this.updateOption(this.optionValueId, this.internalTimeInSeconds);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,13 +94,17 @@ export default class TimeSelector extends OptionsWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async optionsLoaded(options: OptionMap) {
|
async optionsLoaded(options: OptionMap) {
|
||||||
this.internalTimeInSeconds = options[this.optionValueId];
|
const optionValue = optionsService.getInt(this.optionValueId) || 0;
|
||||||
const displayedTime = this.convertTime(options[this.optionValueId], options[this.optionTimeScaleId]).toDisplay();
|
const optionTimeScale = optionsService.getInt(this.optionTimeScaleId) || 1;
|
||||||
|
|
||||||
|
this.setInternalTimeInSeconds(optionValue);
|
||||||
|
|
||||||
|
const displayedTime = this.convertTime(optionValue, optionTimeScale).toDisplay();
|
||||||
this.$timeValueInput.val(displayedTime);
|
this.$timeValueInput.val(displayedTime);
|
||||||
this.$timeScaleSelect.val(options[this.optionTimeScaleId]);
|
this.$timeScaleSelect.val(optionTimeScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
convertTime(time: string | number, timeScale: string | number) {
|
private convertTime(time: string | number, timeScale: string | number) {
|
||||||
const value = typeof time === "number" ? time : parseInt(time);
|
const value = typeof time === "number" ? time : parseInt(time);
|
||||||
if (Number.isNaN(value)) {
|
if (Number.isNaN(value)) {
|
||||||
throw new Error(`Time needs to be a valid integer, but received: ${time}`);
|
throw new Error(`Time needs to be a valid integer, but received: ${time}`);
|
||||||
@ -112,11 +121,20 @@ export default class TimeSelector extends OptionsWidget {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTimeValidation() {
|
private handleTimeValidation() {
|
||||||
if (this.$timeValueInput.is(":invalid")) {
|
if (this.$timeValueInput.is(":invalid")) {
|
||||||
toastService.showError(t("time_selector.invalid_input"));
|
toastService.showError(t("time_selector.invalid_input"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setInternalTimeInSeconds(time: number) {
|
||||||
|
if (time < this.minimumSeconds) {
|
||||||
|
toastService.showError(t("time_selector.minimum_input", {minimumSeconds: this.minimumSeconds}));
|
||||||
|
return this.internalTimeInSeconds = this.minimumSeconds;
|
||||||
|
}
|
||||||
|
return this.internalTimeInSeconds = time;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1658,6 +1658,7 @@
|
|||||||
"days": "Days"
|
"days": "Days"
|
||||||
},
|
},
|
||||||
"time_selector": {
|
"time_selector": {
|
||||||
"invalid_input": "The entered time value is not a valid number."
|
"invalid_input": "The entered time value is not a valid number.",
|
||||||
|
"minimum_input": "The entered time value needs to be at least {{minimumSeconds}} seconds."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ function getType(options: TaskData, mime: string) {
|
|||||||
const mimeLc = mime?.toLowerCase();
|
const mimeLc = mime?.toLowerCase();
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case options.textImportedAsText && ["text/html", "text/markdown", "text/x-markdown"].includes(mimeLc):
|
case options.textImportedAsText && ["text/html", "text/markdown", "text/x-markdown", "text/mdx"].includes(mimeLc):
|
||||||
return "text";
|
return "text";
|
||||||
|
|
||||||
case options.codeImportedAsCode && CODE_MIME_TYPES.has(mimeLc):
|
case options.codeImportedAsCode && CODE_MIME_TYPES.has(mimeLc):
|
||||||
|
21
src/services/import/samples/Text Note.mdx
Normal file
21
src/services/import/samples/Text Note.mdx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Page 1
|
||||||
|
|
||||||
|
Heading 1
|
||||||
|
---------
|
||||||
|
|
||||||
|
Heading 2
|
||||||
|
---------
|
||||||
|
|
||||||
|
### Heading 3
|
||||||
|
|
||||||
|
```
|
||||||
|
class Foo {
|
||||||
|
hoistedNoteChangedEvent({ ntxId }) {
|
||||||
|
if (this.isNoteContext(ntxId)) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Page 2
|
BIN
src/services/import/samples/mdx.zip
Normal file
BIN
src/services/import/samples/mdx.zip
Normal file
Binary file not shown.
49
src/services/import/single.spec.ts
Normal file
49
src/services/import/single.spec.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from "path";
|
||||||
|
import becca from "../../becca/becca.js";
|
||||||
|
import BNote from "../../becca/entities/bnote.js";
|
||||||
|
import TaskContext from "../task_context.js";
|
||||||
|
import cls from "../cls.js";
|
||||||
|
import sql_init from "../sql_init.js";
|
||||||
|
import { initializeTranslations } from "../i18n.js";
|
||||||
|
import single from "./single.js";
|
||||||
|
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
describe("processNoteContent", () => {
|
||||||
|
it("treats single MDX as Markdown", async () => {
|
||||||
|
const mdxSample = fs.readFileSync(path.join(scriptDir, "samples", "Text Note.mdx"));
|
||||||
|
const taskContext = TaskContext.getInstance("import-mdx", "import", {
|
||||||
|
textImportedAsText: true
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
cls.init(async () => {
|
||||||
|
initializeTranslations();
|
||||||
|
sql_init.initializeDb();
|
||||||
|
await sql_init.dbReady;
|
||||||
|
|
||||||
|
const rootNote = becca.getNote("root");
|
||||||
|
if (!rootNote) {
|
||||||
|
reject("Missing root note.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const importedNote = single.importSingleFile(taskContext, {
|
||||||
|
originalname: "Text Note.mdx",
|
||||||
|
mimetype: "text/mdx",
|
||||||
|
buffer: mdxSample
|
||||||
|
}, rootNote as BNote);
|
||||||
|
try {
|
||||||
|
expect(importedNote.mime).toBe("text/html");
|
||||||
|
expect(importedNote.type).toBe("text");
|
||||||
|
expect(importedNote.title).toBe("Text Note");
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
@ -19,7 +19,7 @@ function importSingleFile(taskContext: TaskContext, file: File, parentNote: BNot
|
|||||||
if (taskContext?.data?.textImportedAsText) {
|
if (taskContext?.data?.textImportedAsText) {
|
||||||
if (mime === "text/html") {
|
if (mime === "text/html") {
|
||||||
return importHtml(taskContext, file, parentNote);
|
return importHtml(taskContext, file, parentNote);
|
||||||
} else if (["text/markdown", "text/x-markdown"].includes(mime)) {
|
} else if (["text/markdown", "text/x-markdown", "text/mdx"].includes(mime)) {
|
||||||
return importMarkdown(taskContext, file, parentNote);
|
return importMarkdown(taskContext, file, parentNote);
|
||||||
} else if (mime === "text/plain") {
|
} else if (mime === "text/plain") {
|
||||||
return importPlainText(taskContext, file, parentNote);
|
return importPlainText(taskContext, file, parentNote);
|
||||||
|
46
src/services/import/zip.spec.ts
Normal file
46
src/services/import/zip.spec.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from "path";
|
||||||
|
import zip from "./zip.js";
|
||||||
|
import becca from "../../becca/becca.js";
|
||||||
|
import BNote from "../../becca/entities/bnote.js";
|
||||||
|
import TaskContext from "../task_context.js";
|
||||||
|
import cls from "../cls.js";
|
||||||
|
import sql_init from "../sql_init.js";
|
||||||
|
import { initializeTranslations } from "../i18n.js";
|
||||||
|
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
describe("processNoteContent", () => {
|
||||||
|
it("treats single MDX as Markdown in ZIP as text note", async () => {
|
||||||
|
const mdxSample = fs.readFileSync(path.join(scriptDir, "samples", "mdx.zip"));
|
||||||
|
const taskContext = TaskContext.getInstance("import-mdx", "import", {
|
||||||
|
textImportedAsText: true
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
cls.init(async () => {
|
||||||
|
initializeTranslations();
|
||||||
|
sql_init.initializeDb();
|
||||||
|
await sql_init.dbReady;
|
||||||
|
|
||||||
|
const rootNote = becca.getNote("root");
|
||||||
|
if (!rootNote) {
|
||||||
|
expect(rootNote).toBeTruthy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const importedNote = await zip.importZip(taskContext, mdxSample, rootNote as BNote);
|
||||||
|
try {
|
||||||
|
expect(importedNote.mime).toBe("text/mdx");
|
||||||
|
expect(importedNote.type).toBe("text");
|
||||||
|
expect(importedNote.title).toBe("Text Note");
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
@ -386,7 +386,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processNoteContent(noteMeta: NoteMeta | undefined, type: string, mime: string, content: string | Buffer, noteTitle: string, filePath: string) {
|
function processNoteContent(noteMeta: NoteMeta | undefined, type: string, mime: string, content: string | Buffer, noteTitle: string, filePath: string) {
|
||||||
if ((noteMeta?.format === "markdown" || (!noteMeta && taskContext.data?.textImportedAsText && ["text/markdown", "text/x-markdown"].includes(mime))) && typeof content === "string") {
|
if ((noteMeta?.format === "markdown" || (!noteMeta && taskContext.data?.textImportedAsText && ["text/markdown", "text/x-markdown", "text/mdx"].includes(mime))) && typeof content === "string") {
|
||||||
content = markdownService.renderToHtml(content, noteTitle);
|
content = markdownService.renderToHtml(content, noteTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import { crash } from "./utils.js";
|
|||||||
import resourceDir from "./resource_dir.js";
|
import resourceDir from "./resource_dir.js";
|
||||||
import appInfo from "./app_info.js";
|
import appInfo from "./app_info.js";
|
||||||
import cls from "./cls.js";
|
import cls from "./cls.js";
|
||||||
|
import { t } from "i18next";
|
||||||
|
|
||||||
interface MigrationInfo {
|
interface MigrationInfo {
|
||||||
dbVersion: number;
|
dbVersion: number;
|
||||||
@ -18,9 +19,7 @@ async function migrate() {
|
|||||||
const currentDbVersion = getDbVersion();
|
const currentDbVersion = getDbVersion();
|
||||||
|
|
||||||
if (currentDbVersion < 214) {
|
if (currentDbVersion < 214) {
|
||||||
log.error("Direct migration from your current version is not supported. Please upgrade to the latest v0.60.4 first and only then to this version.");
|
await crash(t("migration.old_version"));
|
||||||
|
|
||||||
await crash();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +82,7 @@ async function migrate() {
|
|||||||
|
|
||||||
log.info(`Migration to version ${mig.dbVersion} has been successful.`);
|
log.info(`Migration to version ${mig.dbVersion} has been successful.`);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
log.error(`error during migration to version ${mig.dbVersion}: ${e.stack}`);
|
crash(t("migration.error_message", { version: mig.dbVersion, stack: e.stack }));
|
||||||
log.error("migration failed, crashing hard"); // this is not very user-friendly :-/
|
|
||||||
|
|
||||||
crash();
|
|
||||||
break; // crash() is sometimes async
|
break; // crash() is sometimes async
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,11 +132,7 @@ async function migrateIfNecessary() {
|
|||||||
const currentDbVersion = getDbVersion();
|
const currentDbVersion = getDbVersion();
|
||||||
|
|
||||||
if (currentDbVersion > appInfo.dbVersion && process.env.TRILIUM_IGNORE_DB_VERSION !== "true") {
|
if (currentDbVersion > appInfo.dbVersion && process.env.TRILIUM_IGNORE_DB_VERSION !== "true") {
|
||||||
log.error(
|
await crash(t("migration.wrong_db_version", { version: currentDbVersion, targetVersion: appInfo.dbVersion }));
|
||||||
`Current DB version ${currentDbVersion} is newer than the current DB version ${appInfo.dbVersion}, which means that it was created by a newer and incompatible version of Trilium. Upgrade to the latest version of Trilium to resolve this issue.`
|
|
||||||
);
|
|
||||||
|
|
||||||
await crash();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDbUpToDate()) {
|
if (!isDbUpToDate()) {
|
||||||
|
@ -10,6 +10,8 @@ import path from "path";
|
|||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { dirname, join } from "path";
|
import { dirname, join } from "path";
|
||||||
import type NoteMeta from "./meta/note_meta.js";
|
import type NoteMeta from "./meta/note_meta.js";
|
||||||
|
import log from "./log.js";
|
||||||
|
import { t } from "i18next";
|
||||||
|
|
||||||
const randtoken = generator({ source: "crypto" });
|
const randtoken = generator({ source: "crypto" });
|
||||||
|
|
||||||
@ -105,10 +107,13 @@ export function escapeRegExp(str: string) {
|
|||||||
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function crash() {
|
export async function crash(message: string) {
|
||||||
if (isElectron) {
|
if (isElectron) {
|
||||||
(await import("electron")).app.exit(1);
|
const electron = await import("electron");
|
||||||
|
electron.dialog.showErrorBox(t("modals.error_title"), message);
|
||||||
|
electron.app.exit(1);
|
||||||
} else {
|
} else {
|
||||||
|
log.error(message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,6 +173,7 @@ export function removeTextFileExtension(filePath: string) {
|
|||||||
|
|
||||||
switch (extension) {
|
switch (extension) {
|
||||||
case ".md":
|
case ".md":
|
||||||
|
case ".mdx":
|
||||||
case ".markdown":
|
case ".markdown":
|
||||||
case ".html":
|
case ".html":
|
||||||
case ".htm":
|
case ".htm":
|
||||||
|
@ -272,5 +272,13 @@
|
|||||||
"today": "Open today's journal note",
|
"today": "Open today's journal note",
|
||||||
"new-note": "New note",
|
"new-note": "New note",
|
||||||
"show-windows": "Show windows"
|
"show-windows": "Show windows"
|
||||||
|
},
|
||||||
|
"migration": {
|
||||||
|
"old_version": "Direct migration from your current version is not supported. Please upgrade to the latest v0.60.4 first and only then to this version.",
|
||||||
|
"error_message": "Error during migration to version {{version}}: {{stack}}",
|
||||||
|
"wrong_db_version": "Current DB version {{version}} is newer than the current DB version {{targetVersion}}, which means that it was created by a newer and incompatible version of Trilium. Upgrade to the latest version of Trilium to resolve this issue."
|
||||||
|
},
|
||||||
|
"modals": {
|
||||||
|
"error_title": "Error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,5 +272,13 @@
|
|||||||
"today": "Mergi la notița de astăzi",
|
"today": "Mergi la notița de astăzi",
|
||||||
"tooltip": "TriliumNext Notes",
|
"tooltip": "TriliumNext Notes",
|
||||||
"show-windows": "Afișează ferestrele"
|
"show-windows": "Afișează ferestrele"
|
||||||
|
},
|
||||||
|
"migration": {
|
||||||
|
"error_message": "Eroare la migrarea către versiunea {{version}}: {{stack}}",
|
||||||
|
"old_version": "Nu se poate migra la ultima versiune direct de la versiunea dvs. Actualizați mai întâi la versiunea v0.60.4 și ulterior la această versiune.",
|
||||||
|
"wrong_db_version": "Versiunea actuală a bazei de date ({{version}}) este mai nouă decât versiunea de bază de date suportată de aplicație ({{targetVersion}}), ceea ce înseamnă că a fost creată de către o versiune mai nouă de Trilium. Actualizați aplicația la ultima versiune pentru a putea continua."
|
||||||
|
},
|
||||||
|
"modals": {
|
||||||
|
"error_title": "Eroare"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user