Merge remote-tracking branch 'origin/develop' into feature/better_sidebar

This commit is contained in:
Elian Doran 2025-01-20 08:36:17 +02:00
commit 74b78e7a2c
No known key found for this signature in database
6 changed files with 62 additions and 45 deletions

View File

@ -464,9 +464,9 @@
"license": "MIT"
},
"node_modules/better-sqlite3": {
"version": "11.8.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.8.0.tgz",
"integrity": "sha512-aKv9s2dir7bsEX5RIjL9HHWB9uQ+f6Vch5B4qmeAOop4Y9OYHX+PNKLr+mpv6+d8L/ZYh4l7H8zPuVMbWkVMLw==",
"version": "11.8.1",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.8.1.tgz",
"integrity": "sha512-9BxNaBkblMjhJW8sMRZxnxVTRgbRmssZW0Oxc1MPBTfiR+WW21e2Mk4qu8CzrcZb1LwPCnFsfDEzq+SNcBU8eg==",
"hasInstallScript": true,
"dependencies": {
"bindings": "^1.5.0",
@ -1516,9 +1516,9 @@
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"better-sqlite3": {
"version": "11.8.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.8.0.tgz",
"integrity": "sha512-aKv9s2dir7bsEX5RIjL9HHWB9uQ+f6Vch5B4qmeAOop4Y9OYHX+PNKLr+mpv6+d8L/ZYh4l7H8zPuVMbWkVMLw==",
"version": "11.8.1",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.8.1.tgz",
"integrity": "sha512-9BxNaBkblMjhJW8sMRZxnxVTRgbRmssZW0Oxc1MPBTfiR+WW21e2Mk4qu8CzrcZb1LwPCnFsfDEzq+SNcBU8eg==",
"requires": {
"bindings": "^1.5.0",
"prebuild-install": "^7.1.1"

31
package-lock.json generated
View File

@ -16,12 +16,12 @@
"@mermaid-js/layout-elk": "0.1.7",
"@mind-elixir/node-menu": "1.0.3",
"@triliumnext/express-partial-content": "1.0.1",
"@types/react-dom": "18.3.1",
"@types/react-dom": "18.3.5",
"archiver": "7.0.1",
"async-mutex": "0.5.0",
"autocomplete.js": "0.38.1",
"axios": "1.7.9",
"better-sqlite3": "11.8.0",
"better-sqlite3": "11.8.1",
"bootstrap": "5.3.3",
"boxicons": "2.1.4",
"cheerio": "1.0.0",
@ -134,7 +134,7 @@
"@types/mime-types": "2.1.4",
"@types/multer": "1.4.12",
"@types/node": "22.10.7",
"@types/react": "18.3.1",
"@types/react": "18.3.18",
"@types/safe-compare": "1.1.2",
"@types/sanitize-html": "2.13.0",
"@types/sax": "1.2.7",
@ -3918,6 +3918,7 @@
"version": "15.7.14",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/qs": {
@ -3935,9 +3936,10 @@
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz",
"integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==",
"version": "18.3.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
"integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@ -3945,12 +3947,12 @@
}
},
"node_modules/@types/react-dom": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==",
"version": "18.3.5",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz",
"integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==",
"license": "MIT",
"dependencies": {
"@types/react": "*"
"peerDependencies": {
"@types/react": "^18.0.0"
}
},
"node_modules/@types/readdir-glob": {
@ -5140,9 +5142,9 @@
"license": "MIT"
},
"node_modules/better-sqlite3": {
"version": "11.8.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.8.0.tgz",
"integrity": "sha512-aKv9s2dir7bsEX5RIjL9HHWB9uQ+f6Vch5B4qmeAOop4Y9OYHX+PNKLr+mpv6+d8L/ZYh4l7H8zPuVMbWkVMLw==",
"version": "11.8.1",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.8.1.tgz",
"integrity": "sha512-9BxNaBkblMjhJW8sMRZxnxVTRgbRmssZW0Oxc1MPBTfiR+WW21e2Mk4qu8CzrcZb1LwPCnFsfDEzq+SNcBU8eg==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@ -6683,6 +6685,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true,
"license": "MIT"
},
"node_modules/cytoscape": {

View File

@ -61,12 +61,12 @@
"@mermaid-js/layout-elk": "0.1.7",
"@mind-elixir/node-menu": "1.0.3",
"@triliumnext/express-partial-content": "1.0.1",
"@types/react-dom": "18.3.1",
"@types/react-dom": "18.3.5",
"archiver": "7.0.1",
"async-mutex": "0.5.0",
"autocomplete.js": "0.38.1",
"axios": "1.7.9",
"better-sqlite3": "11.8.0",
"better-sqlite3": "11.8.1",
"bootstrap": "5.3.3",
"boxicons": "2.1.4",
"cheerio": "1.0.0",
@ -176,7 +176,7 @@
"@types/mime-types": "2.1.4",
"@types/multer": "1.4.12",
"@types/node": "22.10.7",
"@types/react": "18.3.1",
"@types/react": "18.3.18",
"@types/safe-compare": "1.1.2",
"@types/sanitize-html": "2.13.0",
"@types/sax": "1.2.7",

View File

@ -27,7 +27,7 @@ export default class FBlob {
/**
* @throws Error in case of invalid JSON
*/
getJsonContent(): unknown {
getJsonContent<T>(): T | null {
if (!this.content || !this.content.trim()) {
return null;
}

View File

@ -1,6 +1,6 @@
import TypeWidget from "./type_widget.js";
import utils from "../../services/utils.js";
import MindElixir, { type MindElixirCtor } from "mind-elixir";
import type { MindElixirCtor } from "mind-elixir";
import nodeMenu from "@mind-elixir/node-menu";
import type FNote from "../../entities/fnote.js";
import type { EventData } from "../../components/app_context.js";
@ -141,11 +141,16 @@ const TPL = `
</div>
`;
interface MindmapModel {
direction: number;
}
export default class MindMapWidget extends TypeWidget {
private $content!: JQuery<HTMLElement>;
private triggeredByUserOperation?: boolean;
private mind?: ReturnType<MindElixirCtor["new"]>;
private MindElixir: any; // TODO: Fix type
static getType() {
return "mindMap";
@ -170,6 +175,11 @@ export default class MindMapWidget extends TypeWidget {
}
});
// Save the mind map if the user changes the layout direction.
this.$content.on("click", ".mind-elixir-toolbar.lt", () => {
this.spacedUpdate.scheduleUpdate();
});
super.doRender();
}
@ -179,7 +189,6 @@ export default class MindMapWidget extends TypeWidget {
return;
}
this.#initLibrary();
await this.#loadData(note);
}
@ -189,23 +198,27 @@ export default class MindMapWidget extends TypeWidget {
async #loadData(note: FNote) {
const blob = await note.getBlob();
const content = blob?.getJsonContent() || MindElixir.new(NEW_TOPIC_NAME);
const content = blob?.getJsonContent<MindmapModel>();
if (this.mind) {
this.mind.refresh(content);
if (!this.mind) {
await this.#initLibrary(content?.direction);
}
this.mind.refresh(content ?? this.MindElixir.new(NEW_TOPIC_NAME));
this.mind.toCenter();
}
}
#initLibrary() {
const mind = new MindElixir({
async #initLibrary(direction?: number) {
this.MindElixir = (await import("mind-elixir")).default;
const mind = new this.MindElixir({
el: this.$content[0],
direction: MindElixir.LEFT
direction: direction ?? this.MindElixir.LEFT
});
mind.install(nodeMenu);
this.mind = mind;
mind.init(MindElixir.new(NEW_TOPIC_NAME));
mind.init(this.MindElixir.new(NEW_TOPIC_NAME));
// TODO: See why the typeof mindmap is not correct.
mind.bus.addListener("operation", (operation: { name: string }) => {
this.triggeredByUserOperation = true;

View File

@ -61,27 +61,28 @@ describe("data_dir.ts unit tests", async () => {
};
describe("#getPlatformAppDataDir()", () => {
type TestCaseGetPlatformAppDataDir = [description: string, fnValue: Parameters<typeof getPlatformAppDataDir>, expectedValueFn: (val: ReturnType<typeof getPlatformAppDataDir>) => boolean];
type TestCaseGetPlatformAppDataDir = [description: string, fnValue: Parameters<typeof getPlatformAppDataDir>, expectedValue: string | null, osHomedirMockValue: string | null];
const testCases: TestCaseGetPlatformAppDataDir[] = [
["w/ unsupported OS it should return 'null'", ["aix", undefined], (val) => val === null],
["w/ unsupported OS it should return 'null'", ["aix", undefined], null, null],
["w/ win32 and no APPDATA set it should return 'null'", ["win32", undefined], (val) => val === null],
["w/ win32 and no APPDATA set it should return 'null'", ["win32", undefined], null, null],
["w/ win32 and set APPDATA it should return set 'APPDATA'", ["win32", "AppData"], (val) => val === "AppData"],
["w/ win32 and set APPDATA it should return set 'APPDATA'", ["win32", "AppData"], "AppData", null],
["w/ linux it should return '/.local/share'", ["linux", undefined], (val) => val !== null && val.endsWith("/.local/share")],
["w/ linux it should return '~/.local/share'", ["linux", undefined], "/home/mock/.local/share", "/home/mock"],
["w/ linux and wrongly set APPDATA it should ignore APPDATA and return /.local/share", ["linux", "FakeAppData"], (val) => val !== null && val.endsWith("/.local/share")],
["w/ linux and wrongly set APPDATA it should ignore APPDATA and return '~/.local/share'", ["linux", "FakeAppData"], "/home/mock/.local/share", "/home/mock"],
["w/ darwin it should return /Library/Application Support", ["darwin", undefined], (val) => val !== null && val.endsWith("/Library/Application Support")]
["w/ darwin it should return '~/Library/Application Support'", ["darwin", undefined], "/Users/mock/Library/Application Support", "/Users/mock"]
];
testCases.forEach((testCase) => {
const [testDescription, value, isExpected] = testCase;
const [testDescription, fnValues, expected, osHomedirMockValue] = testCase;
return it(testDescription, () => {
const actual = getPlatformAppDataDir(...value);
const result = isExpected(actual);
expect(result).toBeTruthy();
mockFn.osHomedirMock.mockReturnValue(osHomedirMockValue);
const actual = getPlatformAppDataDir(...fnValues);
expect(actual).toEqual(expected);
});
});
});