Merge pull request #928 from TriliumNext/renovate/mind-elixir-4.x

fix(deps): update dependency mind-elixir to v4.3.6
This commit is contained in:
Elian Doran 2025-01-14 20:15:24 +02:00 committed by GitHub
commit 73053a8728
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 98 additions and 78 deletions

View File

@ -16,27 +16,6 @@ env:
TEST_TAG: ${{ github.repository_owner }}/notes:test TEST_TAG: ${{ github.repository_owner }}/notes:test
jobs: jobs:
build_docker:
name: Build Docker image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- run: npm ci
- name: Run the TypeScript build
run: npx tsc
- name: Create server-package.json
run: cat package.json | grep -v electron > server-package.json
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
test_dev: test_dev:
name: Test development name: Test development
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -54,9 +33,34 @@ jobs:
- name: Run the TypeScript build - name: Run the TypeScript build
run: npx tsc run: npx tsc
build_docker:
name: Build Docker image
runs-on: ubuntu-latest
needs:
- test_dev
steps:
- uses: actions/checkout@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- run: npm ci
- name: Run the TypeScript build
run: npx tsc
- name: Create server-package.json
run: cat package.json | grep -v electron > server-package.json
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
test_docker: test_docker:
name: Check Docker build name: Check Docker build
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs:
- build_docker
strategy: strategy:
matrix: matrix:
include: include:

8
package-lock.json generated
View File

@ -70,7 +70,7 @@
"marked": "15.0.6", "marked": "15.0.6",
"mermaid": "11.4.1", "mermaid": "11.4.1",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"mind-elixir": "4.3.5", "mind-elixir": "4.3.6",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
"normalize-strings": "1.1.1", "normalize-strings": "1.1.1",
"normalize.css": "8.0.1", "normalize.css": "8.0.1",
@ -12373,9 +12373,9 @@
} }
}, },
"node_modules/mind-elixir": { "node_modules/mind-elixir": {
"version": "4.3.5", "version": "4.3.6",
"resolved": "https://registry.npmjs.org/mind-elixir/-/mind-elixir-4.3.5.tgz", "resolved": "https://registry.npmjs.org/mind-elixir/-/mind-elixir-4.3.6.tgz",
"integrity": "sha512-I1Mxc/jCwHEDMecDjQVpc+WShmzrEnIv6+MnWPauJ0LAiOXMBQB/wpKqlF4bTp+kCqzOHMYryAvIWm0jvloZ8Q==", "integrity": "sha512-6E9DT5vOYJ7DMDFXJlAnKU3Q6ekwBkR48Tjo6PchEcxJjPURJsiIASxtIeZCfvp8V39N4WyIa3Yt7Q/SFQkVfw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/minimalistic-assert": { "node_modules/minimalistic-assert": {

View File

@ -116,7 +116,7 @@
"marked": "15.0.6", "marked": "15.0.6",
"mermaid": "11.4.1", "mermaid": "11.4.1",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"mind-elixir": "4.3.5", "mind-elixir": "4.3.6",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
"normalize-strings": "1.1.1", "normalize-strings": "1.1.1",
"normalize.css": "8.0.1", "normalize.css": "8.0.1",

View File

@ -92,7 +92,7 @@ export type CommandMappings = {
filePath: string; filePath: string;
}; };
focusAndSelectTitle: CommandData & { focusAndSelectTitle: CommandData & {
isNewNote: boolean; isNewNote?: boolean;
}; };
showPromptDialog: PromptDialogOptions; showPromptDialog: PromptDialogOptions;
showInfoDialog: ConfirmWithMessageOptions; showInfoDialog: ConfirmWithMessageOptions;
@ -262,6 +262,9 @@ type EventMappings = {
}; };
noteContextRemovedEvent: { noteContextRemovedEvent: {
ntxIds: string[]; ntxIds: string[];
};
exportSvg: {
ntxId: string;
} }
}; };
@ -274,15 +277,16 @@ export type CommandListener<T extends CommandNames> = {
}; };
export type CommandListenerData<T extends CommandNames> = CommandMappings[T]; export type CommandListenerData<T extends CommandNames> = CommandMappings[T];
export type EventData<T extends EventNames> = EventMappings[T];
type CommandAndEventMappings = CommandMappings & EventMappings; type CommandAndEventMappings = CommandMappings & EventMappings;
type EventOnlyNames = keyof EventMappings;
export type EventNames = CommandNames | EventOnlyNames;
export type EventData<T extends EventNames> = CommandAndEventMappings[T];
/** /**
* This type is a discriminated union which contains all the possible commands that can be triggered via {@link AppContext.triggerCommand}. * This type is a discriminated union which contains all the possible commands that can be triggered via {@link AppContext.triggerCommand}.
*/ */
export type CommandNames = keyof CommandMappings; export type CommandNames = keyof CommandMappings;
type EventNames = keyof EventMappings;
type FilterByValueType<T, ValueType> = { [K in keyof T]: T[K] extends ValueType ? K : never }[keyof T]; type FilterByValueType<T, ValueType> = { [K in keyof T]: T[K] extends ValueType ? K : never }[keyof T];
@ -375,12 +379,10 @@ class AppContext extends Component {
this.child(rootWidget); this.child(rootWidget);
this.triggerEvent("initialRenderComplete"); this.triggerEvent("initialRenderComplete", {});
} }
// TODO: Remove ignore once all commands are mapped out. triggerEvent<K extends EventNames>(name: K, data: EventData<K>) {
//@ts-ignore
triggerEvent<K extends EventNames | CommandNames>(name: K, data: CommandAndEventMappings[K] = {}) {
return this.handleEvent(name, data); return this.handleEvent(name, data);
} }

View File

@ -1,5 +1,5 @@
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import type { CommandMappings, CommandNames } from "./app_context.js"; import type { CommandMappings, CommandNames, EventData, EventNames } from "./app_context.js";
/** /**
* Abstract class for all components in the Trilium's frontend. * Abstract class for all components in the Trilium's frontend.
@ -46,7 +46,7 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
return this; return this;
} }
handleEvent(name: string, data: unknown): Promise<unknown> | null { handleEvent<T extends EventNames>(name: T, data: EventData<T>): Promise<unknown[] | unknown> | null {
try { try {
const callMethodPromise = this.initialized ? this.initialized.then(() => this.callMethod((this as any)[`${name}Event`], data)) : this.callMethod((this as any)[`${name}Event`], data); const callMethodPromise = this.initialized ? this.initialized.then(() => this.callMethod((this as any)[`${name}Event`], data)) : this.callMethod((this as any)[`${name}Event`], data);
@ -65,11 +65,11 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
return this.parent?.triggerEvent(name, data); return this.parent?.triggerEvent(name, data);
} }
handleEventInChildren(name: string, data: unknown = {}) { handleEventInChildren<T extends EventNames>(name: T, data: EventData<T>): Promise<unknown[] | unknown> | null {
const promises = []; const promises: Promise<unknown>[] = [];
for (const child of this.children) { for (const child of this.children) {
const ret = child.handleEvent(name, data); const ret = child.handleEvent(name, data) as Promise<void>;
if (ret) { if (ret) {
promises.push(ret); promises.push(ret);

View File

@ -35,7 +35,7 @@ const NOTE_TYPE_ICONS = {
* end user. Those types should be used only for checking against, they are * end user. Those types should be used only for checking against, they are
* not for direct use. * not for direct use.
*/ */
type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code"; type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap";
interface NotePathRecord { interface NotePathRecord {
isArchived: boolean; isArchived: boolean;

View File

@ -463,7 +463,7 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
await ws.waitForMaxKnownEntityChangeId(); await ws.waitForMaxKnownEntityChangeId();
await appContext.tabManager.getActiveContext().setNote(notePath); await appContext.tabManager.getActiveContext().setNote(notePath);
await appContext.triggerEvent("focusAndSelectTitle"); await appContext.triggerEvent("focusAndSelectTitle", {});
}; };
this.openTabWithNote = async (notePath, activate) => { this.openTabWithNote = async (notePath, activate) => {
@ -472,7 +472,7 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
await appContext.tabManager.openTabWithNoteWithHoisting(notePath, { activate }); await appContext.tabManager.openTabWithNoteWithHoisting(notePath, { activate });
if (activate) { if (activate) {
await appContext.triggerEvent("focusAndSelectTitle"); await appContext.triggerEvent("focusAndSelectTitle", {});
} }
}; };
@ -485,7 +485,7 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
await appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath }); await appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath });
if (activate) { if (activate) {
await appContext.triggerEvent("focusAndSelectTitle"); await appContext.triggerEvent("focusAndSelectTitle", {});
} }
}; };

View File

@ -96,10 +96,6 @@ const I18NEXT: Library = {
js: ["node_modules/i18next/i18next.min.js", "node_modules/i18next-http-backend/i18nextHttpBackend.min.js"] js: ["node_modules/i18next/i18next.min.js", "node_modules/i18next-http-backend/i18nextHttpBackend.min.js"]
}; };
const MIND_ELIXIR: Library = {
js: ["node_modules/mind-elixir/dist/MindElixir.iife.js", "node_modules/@mind-elixir/node-menu/dist/node-menu.umd.cjs"]
};
const HIGHLIGHT_JS: Library = { const HIGHLIGHT_JS: Library = {
js: () => { js: () => {
const mimeTypes = mimeTypesService.getMimeTypes(); const mimeTypes = mimeTypesService.getMimeTypes();
@ -219,6 +215,5 @@ export default {
EXCALIDRAW, EXCALIDRAW,
MARKJS, MARKJS,
I18NEXT, I18NEXT,
MIND_ELIXIR,
HIGHLIGHT_JS HIGHLIGHT_JS
}; };

View File

@ -74,9 +74,9 @@ ws.subscribeToMessages(async (message) => {
if (message.type === "protectedSessionLogin") { if (message.type === "protectedSessionLogin") {
await reloadData(); await reloadData();
await appContext.triggerEvent("frocaReloaded"); await appContext.triggerEvent("frocaReloaded", {});
appContext.triggerEvent("protectedSessionStarted"); appContext.triggerEvent("protectedSessionStarted", {});
appContext.triggerCommand("closeProtectedSessionPasswordDialog"); appContext.triggerCommand("closeProtectedSessionPasswordDialog");

View File

@ -20,3 +20,7 @@ declare module "draggabilly" {
destroy(); destroy();
} }
} }
declare module '@mind-elixir/node-menu' {
export default mindmap;
}

View File

@ -51,7 +51,7 @@ export default class LauncherContainer extends FlexContainer<LauncherWidget> {
this.$widget.empty(); this.$widget.empty();
this.renderChildren(); this.renderChildren();
await this.handleEventInChildren("initialRenderComplete"); await this.handleEventInChildren("initialRenderComplete", {});
const activeContext = appContext.tabManager.getActiveContext(); const activeContext = appContext.tabManager.getActiveContext();

View File

@ -1,6 +1,7 @@
import FlexContainer from "./flex_container.js"; import FlexContainer from "./flex_container.js";
import splitService from "../../services/resizer.js"; import splitService from "../../services/resizer.js";
import type RightPanelWidget from "../right_panel_widget.js"; import type RightPanelWidget from "../right_panel_widget.js";
import type { EventData, EventNames } from "../../components/app_context.js";
export default class RightPaneContainer extends FlexContainer<RightPanelWidget> { export default class RightPaneContainer extends FlexContainer<RightPanelWidget> {
private rightPaneHidden: boolean; private rightPaneHidden: boolean;
@ -19,7 +20,7 @@ export default class RightPaneContainer extends FlexContainer<RightPanelWidget>
return super.isEnabled() && !this.rightPaneHidden && this.children.length > 0 && !!this.children.find((ch) => ch.isEnabled() && ch.canBeShown()); return super.isEnabled() && !this.rightPaneHidden && this.children.length > 0 && !!this.children.find((ch) => ch.isEnabled() && ch.canBeShown());
} }
handleEventInChildren(name: string, data: unknown) { handleEventInChildren<T extends EventNames>(name: T, data: EventData<T>): Promise<unknown[] | unknown> | null {
const promise = super.handleEventInChildren(name, data); const promise = super.handleEventInChildren(name, data);
if (["activeContextChanged", "noteSwitchedAndActivated", "noteSwitched"].includes(name)) { if (["activeContextChanged", "noteSwitchedAndActivated", "noteSwitched"].includes(name)) {

View File

@ -1,6 +1,11 @@
import libraryLoader from "../../services/library_loader.js";
import TypeWidget from "./type_widget.js"; import TypeWidget from "./type_widget.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import MindElixir, { 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";
const NEW_TOPIC_NAME = "";
const TPL = ` const TPL = `
<div class="note-detail-mind-map note-detail-printable"> <div class="note-detail-mind-map note-detail-printable">
@ -137,6 +142,11 @@ const TPL = `
`; `;
export default class MindMapWidget extends TypeWidget { export default class MindMapWidget extends TypeWidget {
private $content!: JQuery<HTMLElement>;
private triggeredByUserOperation?: boolean;
private mind?: ReturnType<MindElixirCtor["new"]>;
static getType() { static getType() {
return "mindMap"; return "mindMap";
} }
@ -163,16 +173,12 @@ export default class MindMapWidget extends TypeWidget {
super.doRender(); super.doRender();
} }
async doRefresh(note) { async doRefresh(note: FNote) {
if (this.triggeredByUserOperation) { if (this.triggeredByUserOperation) {
this.triggeredByUserOperation = false; this.triggeredByUserOperation = false;
return; return;
} }
if (!window.MindElixir) {
await libraryLoader.requireLibrary(libraryLoader.MIND_ELIXIR);
}
this.#initLibrary(); this.#initLibrary();
await this.#loadData(note); await this.#loadData(note);
} }
@ -181,12 +187,14 @@ export default class MindMapWidget extends TypeWidget {
this.triggeredByUserOperation = false; this.triggeredByUserOperation = false;
} }
async #loadData(note) { async #loadData(note: FNote) {
const blob = await note.getBlob(); const blob = await note.getBlob();
const content = blob.getJsonContent() || MindElixir.new(); const content = blob?.getJsonContent() || MindElixir.new(NEW_TOPIC_NAME);
this.mind.refresh(content); if (this.mind) {
this.mind.toCenter(); this.mind.refresh(content);
this.mind.toCenter();
}
} }
#initLibrary() { #initLibrary() {
@ -194,11 +202,12 @@ export default class MindMapWidget extends TypeWidget {
el: this.$content[0], el: this.$content[0],
direction: MindElixir.LEFT direction: MindElixir.LEFT
}); });
mind.install(window["@mind-elixir/node-menu"]); mind.install(nodeMenu);
this.mind = mind; this.mind = mind;
mind.init(MindElixir.new()); mind.init(MindElixir.new(NEW_TOPIC_NAME));
mind.bus.addListener("operation", (operation) => { // TODO: See why the typeof mindmap is not correct.
mind.bus.addListener("operation", (operation: { name: string }) => {
this.triggeredByUserOperation = true; this.triggeredByUserOperation = true;
if (operation.name !== "beginEdit") { if (operation.name !== "beginEdit") {
this.spacedUpdate.scheduleUpdate(); this.spacedUpdate.scheduleUpdate();
@ -237,14 +246,14 @@ export default class MindMapWidget extends TypeWidget {
return await this.mind.exportSvg().text(); return await this.mind.exportSvg().text();
} }
async entitiesReloadedEvent({ loadResults }) { async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded"> ) {
if (loadResults.isNoteReloaded(this.noteId)) { if (this.noteId && loadResults.isNoteReloaded(this.noteId)) {
this.refresh(); this.refresh();
} }
} }
async exportSvgEvent({ ntxId }) { async exportSvgEvent({ ntxId }: EventData<"exportSvg">) {
if (!this.isNoteContext(ntxId) || this.note.type !== "mindMap") { if (!this.isNoteContext(ntxId) || this.note?.type !== "mindMap") {
return; return;
} }

View File

@ -1,7 +1,13 @@
import NoteContextAwareWidget from "../note_context_aware_widget.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js";
import appContext from "../../components/app_context.js"; import appContext, { type EventData, type EventNames } from "../../components/app_context.js";
import type FNote from "../../entities/fnote.js";
import type NoteDetailWidget from "../note_detail.js";
import type SpacedUpdate from "../../services/spaced_update.js";
export default abstract class TypeWidget extends NoteContextAwareWidget {
protected spacedUpdate!: SpacedUpdate;
export default class TypeWidget extends NoteContextAwareWidget {
// for overriding // for overriding
static getType() {} static getType() {}
@ -11,12 +17,11 @@ export default class TypeWidget extends NoteContextAwareWidget {
return super.doRender(); return super.doRender();
} }
/** @param {FNote} note */ abstract doRefresh(note: FNote | null | undefined): Promise<void>;
async doRefresh(note) {}
async refresh() { async refresh() {
const thisWidgetType = this.constructor.getType(); const thisWidgetType = (this.constructor as any).getType();
const noteWidgetType = await this.parent.getWidgetType(); const noteWidgetType = await (this.parent as NoteDetailWidget).getWidgetType();
if (thisWidgetType !== noteWidgetType) { if (thisWidgetType !== noteWidgetType) {
this.toggleInt(false); this.toggleInt(false);
@ -27,7 +32,7 @@ export default class TypeWidget extends NoteContextAwareWidget {
await this.doRefresh(this.note); await this.doRefresh(this.note);
this.triggerEvent("noteDetailRefreshed", { ntxId: this.noteContext.ntxId }); this.triggerEvent("noteDetailRefreshed", { ntxId: this.noteContext?.ntxId });
} }
} }
@ -40,7 +45,7 @@ export default class TypeWidget extends NoteContextAwareWidget {
focus() {} focus() {}
async readOnlyTemporarilyDisabledEvent({ noteContext }) { async readOnlyTemporarilyDisabledEvent({ noteContext }: EventData<"readOnlyTemporarilyDisabled">) {
if (this.isNoteContext(noteContext.ntxId)) { if (this.isNoteContext(noteContext.ntxId)) {
await this.refresh(); await this.refresh();
@ -49,10 +54,10 @@ export default class TypeWidget extends NoteContextAwareWidget {
} }
// events should be propagated manually to the children widgets // events should be propagated manually to the children widgets
handleEventInChildren(name, data) { handleEventInChildren<T extends EventNames>(name: T, data: EventData<T>) {
if (["activeContextChanged", "setNoteContext"].includes(name)) { if (["activeContextChanged", "setNoteContext"].includes(name)) {
// won't trigger .refresh(); // won't trigger .refresh();
return super.handleEventInChildren("setNoteContext", data); return super.handleEventInChildren("setNoteContext", data as EventData<"activeContextChanged">);
} else if (name === "entitiesReloaded") { } else if (name === "entitiesReloaded") {
return super.handleEventInChildren(name, data); return super.handleEventInChildren(name, data);
} else { } else {