mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-10-02 02:41:34 +08:00
Merge remote-tracking branch 'origin/develop' into feature/better_sidebar
This commit is contained in:
commit
74b78e7a2c
12
dump-db/package-lock.json
generated
12
dump-db/package-lock.json
generated
@ -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
31
package-lock.json
generated
@ -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": {
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user