From f01bebedc9f65a59d77bf76fb639286ec70a9216 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 21 Mar 2026 18:28:35 +0200 Subject: [PATCH 01/83] client: create a Vite plugin to generate a contributor list JSON at the build time --- apps/client/src/services/contributors_list.ts | 3 +++ apps/client/vite.config.mts | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 apps/client/src/services/contributors_list.ts diff --git a/apps/client/src/services/contributors_list.ts b/apps/client/src/services/contributors_list.ts new file mode 100644 index 0000000000..d2daf1921a --- /dev/null +++ b/apps/client/src/services/contributors_list.ts @@ -0,0 +1,3 @@ +export default async function getContributors() { + return {contributors: []} +} \ No newline at end of file diff --git a/apps/client/vite.config.mts b/apps/client/vite.config.mts index 8c6debfc08..e9577ac2e0 100644 --- a/apps/client/vite.config.mts +++ b/apps/client/vite.config.mts @@ -1,13 +1,28 @@ /// import prefresh from '@prefresh/vite'; -import { join } from 'path'; +import { join, resolve } from "path"; import webpackStatsPlugin from 'rollup-plugin-webpack-stats'; import { defineConfig } from 'vite'; import { viteStaticCopy } from 'vite-plugin-static-copy' +import { writeFileSync, mkdirSync } from "fs"; +import getContributors from "./src/services/contributors_list"; const assets = [ "assets", "stylesheets", "fonts", "translations" ]; const isDev = process.env.NODE_ENV === "development"; + +const buildContributorListPlugin = { + name: "build-contributor-list-plugin", + writeBundle: async () => { + console.log("Retrieving the contributors list..."); + const jsonData = await getContributors(); + + const assetsDir = resolve(__dirname, "dist/assets"); + mkdirSync(assetsDir, {recursive: true}); + writeFileSync(resolve(assetsDir, "contributors.json"), JSON.stringify(jsonData, null, 2)); + } +}; + let plugins: any = []; if (isDev) { @@ -17,6 +32,7 @@ if (isDev) { ]; } else { plugins = [ + buildContributorListPlugin, viteStaticCopy({ targets: assets.map((asset) => ({ src: `src/${asset}/*`, From 4d753398c1ae60be64b6bf593a0099a9ef1ace2d Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 21 Mar 2026 18:30:33 +0200 Subject: [PATCH 02/83] refactor: normalize to double quotes --- apps/client/vite.config.mts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/client/vite.config.mts b/apps/client/vite.config.mts index e9577ac2e0..29ad43db20 100644 --- a/apps/client/vite.config.mts +++ b/apps/client/vite.config.mts @@ -1,9 +1,9 @@ -/// -import prefresh from '@prefresh/vite'; +/// +import prefresh from "@prefresh/vite"; import { join, resolve } from "path"; -import webpackStatsPlugin from 'rollup-plugin-webpack-stats'; -import { defineConfig } from 'vite'; -import { viteStaticCopy } from 'vite-plugin-static-copy' +import webpackStatsPlugin from "rollup-plugin-webpack-stats"; +import { defineConfig } from "vite"; +import { viteStaticCopy } from "vite-plugin-static-copy" import { writeFileSync, mkdirSync } from "fs"; import getContributors from "./src/services/contributors_list"; @@ -54,17 +54,17 @@ if (isDev) { export default defineConfig(() => ({ root: __dirname, - cacheDir: '../../.cache/vite', + cacheDir: "../../.cache/vite", base: "", plugins, // Use esbuild for JSX transformation (much faster than Babel) esbuild: { - jsx: 'automatic', - jsxImportSource: 'preact', + jsx: "automatic", + jsxImportSource: "preact", jsxDev: isDev }, css: { - transformer: 'lightningcss', + transformer: "lightningcss", devSourcemap: isDev }, resolve: { @@ -95,7 +95,7 @@ export default defineConfig(() => ({ }, build: { target: "esnext", - outDir: './dist', + outDir: "./dist", emptyOutDir: true, reportCompressedSize: true, sourcemap: false, From 6481b90daf22aec878b41b0f2fa6040daf7fe4d2 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 21 Mar 2026 19:59:19 +0200 Subject: [PATCH 03/83] client: retrieve the contributor list --- apps/client/src/services/contributors_list.ts | 29 ++++++++++++++++++- apps/client/vite.config.mts | 10 +++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/apps/client/src/services/contributors_list.ts b/apps/client/src/services/contributors_list.ts index d2daf1921a..060b9c6dda 100644 --- a/apps/client/src/services/contributors_list.ts +++ b/apps/client/src/services/contributors_list.ts @@ -1,3 +1,30 @@ +export interface ContributorList { + contributors: Contributor[]; +} + +export interface Contributor { + name: string; + url: string; +} + export default async function getContributors() { - return {contributors: []} + const response = await fetch("https://api.github.com/repos/TriliumNext/Trilium/contributors"); + + if (response.ok) { + return { + contributors: getList(await response.json()) + } as ContributorList + } else { + throw new Error(`Unable to request the contributor list from GitHub. Reason: ${response.statusText}`); + } +} + +function getList(contributorInfo: any[]) { + return contributorInfo + .filter((c) => c.type === "User" && c.user_view_type === "public") + .sort((a, b) => b.contributions - a.contributions) + .map((c) => {return { + name: c.login, + url: c.html_url + } as Contributor}); } \ No newline at end of file diff --git a/apps/client/vite.config.mts b/apps/client/vite.config.mts index 29ad43db20..08effeea2f 100644 --- a/apps/client/vite.config.mts +++ b/apps/client/vite.config.mts @@ -14,8 +14,14 @@ const isDev = process.env.NODE_ENV === "development"; const buildContributorListPlugin = { name: "build-contributor-list-plugin", writeBundle: async () => { - console.log("Retrieving the contributors list..."); - const jsonData = await getContributors(); + console.log("Retrieving the contributor list..."); + + let jsonData: any = {}; + try { + jsonData = await getContributors(); + } catch (ex) { + console.error(ex); + } const assetsDir = resolve(__dirname, "dist/assets"); mkdirSync(assetsDir, {recursive: true}); From 92f10489115f410541a1da74a4d30fcfc502c2d3 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 21 Mar 2026 20:19:17 +0200 Subject: [PATCH 04/83] client/contributor list: add support for honorific contributors --- apps/client/src/services/contributors_list.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/client/src/services/contributors_list.ts b/apps/client/src/services/contributors_list.ts index 060b9c6dda..ab1f6c1755 100644 --- a/apps/client/src/services/contributors_list.ts +++ b/apps/client/src/services/contributors_list.ts @@ -7,6 +7,10 @@ export interface Contributor { url: string; } +// Keep honorific contributors at top of the list, even if their commit count +// is exceeded by another users. +const PINNED_CONTRIBUTORS = ["eliandoran", "zadam"]; + export default async function getContributors() { const response = await fetch("https://api.github.com/repos/TriliumNext/Trilium/contributors"); @@ -21,10 +25,25 @@ export default async function getContributors() { function getList(contributorInfo: any[]) { return contributorInfo + // Filter out bots and private profiles .filter((c) => c.type === "User" && c.user_view_type === "public") - .sort((a, b) => b.contributions - a.contributions) + // Sort by the commit count. Honorific contributors are always first. + .sort(contributorOrderer) .map((c) => {return { name: c.login, url: c.html_url } as Contributor}); +} + +function contributorOrderer(a, b) { + const isAPinned = PINNED_CONTRIBUTORS.includes(a.login); + const isBPinned = PINNED_CONTRIBUTORS.includes(b.login); + + // Pinned contributors come first + if (isAPinned !== isBPinned) { + return isAPinned ? -1 : 1; + } + + // Within each group, sort by contributions + return b.contributions - a.contributions; } \ No newline at end of file From fb691f6ade854d300562a42f088299d3bda97a5d Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 21 Mar 2026 20:28:36 +0200 Subject: [PATCH 05/83] client/contributor list: add extra bot exclusion --- apps/client/src/services/contributors_list.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/client/src/services/contributors_list.ts b/apps/client/src/services/contributors_list.ts index ab1f6c1755..7e1a4fb98a 100644 --- a/apps/client/src/services/contributors_list.ts +++ b/apps/client/src/services/contributors_list.ts @@ -11,6 +11,9 @@ export interface Contributor { // is exceeded by another users. const PINNED_CONTRIBUTORS = ["eliandoran", "zadam"]; +// Bots marked as users on the GitHub profile info to exclude from the listing +const BOTS = ["weblate"]; + export default async function getContributors() { const response = await fetch("https://api.github.com/repos/TriliumNext/Trilium/contributors"); @@ -26,7 +29,7 @@ export default async function getContributors() { function getList(contributorInfo: any[]) { return contributorInfo // Filter out bots and private profiles - .filter((c) => c.type === "User" && c.user_view_type === "public") + .filter((c) => c.type === "User" && c.user_view_type === "public" && !BOTS.includes(c.login)) // Sort by the commit count. Honorific contributors are always first. .sort(contributorOrderer) .map((c) => {return { From 8d5e82fa5e1263df3bb281827950eded8bf0c44a Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 10:25:35 +0200 Subject: [PATCH 06/83] client/contributor list: refine filtering --- apps/client/src/services/contributors_list.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/client/src/services/contributors_list.ts b/apps/client/src/services/contributors_list.ts index 7e1a4fb98a..902d0538ca 100644 --- a/apps/client/src/services/contributors_list.ts +++ b/apps/client/src/services/contributors_list.ts @@ -28,8 +28,8 @@ export default async function getContributors() { function getList(contributorInfo: any[]) { return contributorInfo - // Filter out bots and private profiles - .filter((c) => c.type === "User" && c.user_view_type === "public" && !BOTS.includes(c.login)) + // Filter out bots + .filter((c) => c.type === "User" && !BOTS.includes(c.login)) // Sort by the commit count. Honorific contributors are always first. .sort(contributorOrderer) .map((c) => {return { From 31578521cfa66ed61923092feffdb70bf6b009d2 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 10:27:27 +0200 Subject: [PATCH 07/83] client/about dialog: create a new UI --- .../src/translations/en/translation.json | 8 +- apps/client/src/widgets/dialogs/about.css | 11 +++ apps/client/src/widgets/dialogs/about.tsx | 80 +++++++++---------- 3 files changed, 57 insertions(+), 42 deletions(-) create mode 100644 apps/client/src/widgets/dialogs/about.css diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 27891a02ab..fa10640cc8 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -7,7 +7,13 @@ "sync_version": "Sync version:", "build_date": "Build date:", "build_revision": "Build revision:", - "data_directory": "Data directory:" + "data_directory": "Data directory:", + + "version_label": "Version", + "version": "app: {{appVersion}}, database: {{dbVersion}}, sync protocol: {{syncVersion}}", + "build_info": "{{buildDate}}, revision: {{buildRevision}}", + "contributors_label": "Contributors" + }, "toast": { "critical-error": { diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css new file mode 100644 index 0000000000..2ea9eeb628 --- /dev/null +++ b/apps/client/src/widgets/dialogs/about.css @@ -0,0 +1,11 @@ +.property-sheet-card { + font-size: .85em; + + .tn-card-section { + display: flex; + + > :first-child { + width: 100px; + } + } +} \ No newline at end of file diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index f09cca3191..47da8e9cf8 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -8,18 +8,19 @@ import { useState } from "preact/hooks"; import type { CSSProperties } from "preact/compat"; import type { AppInfo } from "@triliumnext/commons"; import { useTriliumEvent } from "../react/hooks.jsx"; +import logo from "../../assets/icon.png"; +import { Card, CardSection } from "../react/Card.js"; +import "./about.css"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); const [shown, setShown] = useState(false); - const forceWordBreak: CSSProperties = { wordBreak: "break-all" }; useTriliumEvent("openAboutDialog", () => setShown(true)); return ( { const appInfo = await server.get("app-info"); @@ -27,44 +28,41 @@ export default function AboutDialog() { }} onHidden={() => setShown(false)} > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{t("about.homepage")}https://github.com/TriliumNext/Trilium
{t("about.app_version")}{appInfo?.appVersion}
{t("about.db_version")}{appInfo?.dbVersion}
{t("about.sync_version")}{appInfo?.syncVersion}
{t("about.build_date")} - {appInfo?.buildDate ? formatDateTime(appInfo.buildDate) : ""} -
{t("about.build_revision")} - {appInfo?.buildRevision && {appInfo.buildRevision}} -
{t("about.data_directory")} - {appInfo?.dataDirectory && ()} -
+
+ +

Trilium Notes

+ + triliumnotes.org + + + + +
{t("about.version_label")}
+
+ {t("about.version", { + appVersion: appInfo?.appVersion, + dbVersion: appInfo?.dbVersion, + syncVersion: appInfo?.syncVersion + })} +
+ {t("about.build_info", { + buildDate: appInfo?.buildDate ? formatDateTime(appInfo.buildDate) : "", + buildRevision: appInfo?.buildRevision ? appInfo.buildRevision.substring(0, 6) : "" + })} +
+
+
+ +
{t("about.contributors_label")}
+
+ +
{t("about.data_directory")}
+
+ {appInfo?.dataDirectory && ()} +
+
+
+
); } From 19dfbaacce1ffc7ccc0167cfaf9388c95b5bfb68 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 11:14:16 +0200 Subject: [PATCH 08/83] client/about dialog: improve, add GitHub and donate buttons --- .../src/translations/en/translation.json | 18 +++------ apps/client/src/widgets/dialogs/about.css | 38 ++++++++++++++++--- apps/client/src/widgets/dialogs/about.tsx | 18 ++++++++- 3 files changed, 53 insertions(+), 21 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index fa10640cc8..5732fb857f 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1,19 +1,11 @@ { - "about": { - "title": "About Trilium Notes", - "homepage": "Homepage:", - "app_version": "App version:", - "db_version": "DB version:", - "sync_version": "Sync version:", - "build_date": "Build date:", - "build_revision": "Build revision:", - "data_directory": "Data directory:", - - "version_label": "Version", + "about": { + "version_label": "Version:", "version": "app: {{appVersion}}, database: {{dbVersion}}, sync protocol: {{syncVersion}}", "build_info": "{{buildDate}}, revision: {{buildRevision}}", - "contributors_label": "Contributors" - + "contributors_label": "Contributors:", + "data_directory": "Data directory:", + "donate": "Donate" }, "toast": { "critical-error": { diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 2ea9eeb628..09b22b9ead 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -1,11 +1,37 @@ -.property-sheet-card { - font-size: .85em; - - .tn-card-section { +.about-dialog { + .about-dialog-content { display: flex; + flex-direction: column; + align-items: center; + } - > :first-child { - width: 100px; + .property-sheet-card { + margin-block: 30px; + font-size: .85em; + + .tn-card-section { + display: flex; + + > :first-child { + width: 100px; + } + } + } + + footer { + display: flex; + justify-content: center; + gap: 20px; + margin-bottom: 30px; + + a { + display: flex; + flex-direction: column; + align-items: center; + + &::after { + display: none; + } } } } \ No newline at end of file diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 47da8e9cf8..a8bce73e48 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -28,13 +28,14 @@ export default function AboutDialog() { }} onHidden={() => setShown(false)} > -
+
+

Trilium Notes

triliumnotes.org - +
{t("about.version_label")}
@@ -63,6 +64,19 @@ export default function AboutDialog() {
+ + ); } From 75da044bbefc6b033fd8ffdddea8adb3eff21815 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 17:21:20 +0200 Subject: [PATCH 09/83] client/about dialog: add a link for the build revision --- .../src/translations/en/translation.json | 2 +- apps/client/src/widgets/dialogs/about.tsx | 23 ++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 5732fb857f..e1271388b3 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -2,7 +2,7 @@ "about": { "version_label": "Version:", "version": "app: {{appVersion}}, database: {{dbVersion}}, sync protocol: {{syncVersion}}", - "build_info": "{{buildDate}}, revision: {{buildRevision}}", + "build_info": "{{buildDate}}, revision: ", "contributors_label": "Contributors:", "data_directory": "Data directory:", "donate": "Donate" diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index a8bce73e48..bb6238108f 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -11,6 +11,8 @@ import { useTriliumEvent } from "../react/hooks.jsx"; import logo from "../../assets/icon.png"; import { Card, CardSection } from "../react/Card.js"; import "./about.css"; +import { Trans } from "react-i18next"; +import type React from "react"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); @@ -39,17 +41,26 @@ export default function AboutDialog() {
{t("about.version_label")}
-
+
{t("about.version", { appVersion: appInfo?.appVersion, dbVersion: appInfo?.dbVersion, syncVersion: appInfo?.syncVersion })}
- {t("about.build_info", { - buildDate: appInfo?.buildDate ? formatDateTime(appInfo.buildDate) : "", - buildRevision: appInfo?.buildRevision ? appInfo.buildRevision.substring(0, 6) : "" - })} + + {appInfo?.buildRevision && + {appInfo.buildRevision.substring(0, 7)} + } + as React.ReactElement + }} + />
@@ -58,7 +69,7 @@ export default function AboutDialog() {
{t("about.data_directory")}
-
+
{appInfo?.dataDirectory && ()}
From 8f2387462871e225c27b8312a94c1982c568e1a7 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 18:17:13 +0200 Subject: [PATCH 10/83] client/about dialog: add an indication for the nightly version, use SVG icons --- apps/client/src/assets/icon-alt.svg | 17 ++++++++++++++ apps/client/src/assets/icon.svg | 28 +++++++++++++++++++++++ apps/client/src/widgets/dialogs/about.tsx | 13 ++++++++--- 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 apps/client/src/assets/icon-alt.svg create mode 100644 apps/client/src/assets/icon.svg diff --git a/apps/client/src/assets/icon-alt.svg b/apps/client/src/assets/icon-alt.svg new file mode 100644 index 0000000000..dae45a4222 --- /dev/null +++ b/apps/client/src/assets/icon-alt.svg @@ -0,0 +1,17 @@ + + + Trilium Notes + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/client/src/assets/icon.svg b/apps/client/src/assets/icon.svg new file mode 100644 index 0000000000..943f1bfe89 --- /dev/null +++ b/apps/client/src/assets/icon.svg @@ -0,0 +1,28 @@ + + + Trilium Notes + + + + + + + + + + + + + + + diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index bb6238108f..f9ba761e2d 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -8,18 +8,25 @@ import { useState } from "preact/hooks"; import type { CSSProperties } from "preact/compat"; import type { AppInfo } from "@triliumnext/commons"; import { useTriliumEvent } from "../react/hooks.jsx"; -import logo from "../../assets/icon.png"; import { Card, CardSection } from "../react/Card.js"; import "./about.css"; import { Trans } from "react-i18next"; import type React from "react"; +import icon from "../../assets/icon.svg"; +import iconAlt from "../../assets/icon-alt.svg"; +import { useEffect } from "react"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); const [shown, setShown] = useState(false); + const [isNightly, setNightly] = useState(false); useTriliumEvent("openAboutDialog", () => setShown(true)); + useEffect(() => { + setNightly(!!appInfo?.appVersion.includes("test")); + }, [appInfo]) + return (
- -

Trilium Notes

+ +

Trilium Notes {isNightly && Nightly}

triliumnotes.org From 1b9124422a29f96af80440a9bdedbd782f4e5ce8 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 18:40:26 +0200 Subject: [PATCH 11/83] client/about dialog: switch to boxicons --- apps/client/src/widgets/dialogs/about.css | 4 ++++ apps/client/src/widgets/dialogs/about.tsx | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 09b22b9ead..d6113c9bc1 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -32,6 +32,10 @@ &::after { display: none; } + + i { + font-size: 32px; + } } } } \ No newline at end of file diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index f9ba761e2d..69783e198c 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -85,13 +85,11 @@ export default function AboutDialog() { From 555d997e34a37f5af14a41fa3565bf14f425f170 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 18:53:23 +0200 Subject: [PATCH 12/83] client/about dialog: show the dialog only after the required information is ready --- apps/client/src/widgets/dialogs/about.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 69783e198c..d47e8167a4 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -14,14 +14,21 @@ import { Trans } from "react-i18next"; import type React from "react"; import icon from "../../assets/icon.svg"; import iconAlt from "../../assets/icon-alt.svg"; -import { useEffect } from "react"; +import { useCallback, useEffect } from "react"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); const [shown, setShown] = useState(false); const [isNightly, setNightly] = useState(false); - useTriliumEvent("openAboutDialog", () => setShown(true)); + const onLoad = useCallback(async () => { + if (!appInfo) { + setAppInfo(await server.get("app-info")); + } + setShown(true); + }, []); + + useTriliumEvent("openAboutDialog", onLoad); useEffect(() => { setNightly(!!appInfo?.appVersion.includes("test")); @@ -31,10 +38,6 @@ export default function AboutDialog() { { - const appInfo = await server.get("app-info"); - setAppInfo(appInfo); - }} onHidden={() => setShown(false)} >
From b4d0af6eb2da7e6ca3f016aacf0b6698009ab43b Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 18:55:46 +0200 Subject: [PATCH 13/83] client/modals: make the title optional --- apps/client/src/widgets/react/Modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/react/Modal.tsx b/apps/client/src/widgets/react/Modal.tsx index e7a7c721f8..91bc690049 100644 --- a/apps/client/src/widgets/react/Modal.tsx +++ b/apps/client/src/widgets/react/Modal.tsx @@ -16,7 +16,7 @@ interface CustomTitleBarButton { export interface ModalProps { className: string; - title: string | ComponentChildren; + title?: string | ComponentChildren; customTitleBarButtons?: (CustomTitleBarButton | null)[]; size: "xl" | "lg" | "md" | "sm"; children: ComponentChildren; From 978e02350c9f589a83726e549350190ca8c401c1 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 19:14:44 +0200 Subject: [PATCH 14/83] client: revert the Vite build script --- apps/client/vite.config.mts | 44 ++++++++++--------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/apps/client/vite.config.mts b/apps/client/vite.config.mts index 08effeea2f..8c6debfc08 100644 --- a/apps/client/vite.config.mts +++ b/apps/client/vite.config.mts @@ -1,34 +1,13 @@ -/// -import prefresh from "@prefresh/vite"; -import { join, resolve } from "path"; -import webpackStatsPlugin from "rollup-plugin-webpack-stats"; -import { defineConfig } from "vite"; -import { viteStaticCopy } from "vite-plugin-static-copy" -import { writeFileSync, mkdirSync } from "fs"; -import getContributors from "./src/services/contributors_list"; +/// +import prefresh from '@prefresh/vite'; +import { join } from 'path'; +import webpackStatsPlugin from 'rollup-plugin-webpack-stats'; +import { defineConfig } from 'vite'; +import { viteStaticCopy } from 'vite-plugin-static-copy' const assets = [ "assets", "stylesheets", "fonts", "translations" ]; const isDev = process.env.NODE_ENV === "development"; - -const buildContributorListPlugin = { - name: "build-contributor-list-plugin", - writeBundle: async () => { - console.log("Retrieving the contributor list..."); - - let jsonData: any = {}; - try { - jsonData = await getContributors(); - } catch (ex) { - console.error(ex); - } - - const assetsDir = resolve(__dirname, "dist/assets"); - mkdirSync(assetsDir, {recursive: true}); - writeFileSync(resolve(assetsDir, "contributors.json"), JSON.stringify(jsonData, null, 2)); - } -}; - let plugins: any = []; if (isDev) { @@ -38,7 +17,6 @@ if (isDev) { ]; } else { plugins = [ - buildContributorListPlugin, viteStaticCopy({ targets: assets.map((asset) => ({ src: `src/${asset}/*`, @@ -60,17 +38,17 @@ if (isDev) { export default defineConfig(() => ({ root: __dirname, - cacheDir: "../../.cache/vite", + cacheDir: '../../.cache/vite', base: "", plugins, // Use esbuild for JSX transformation (much faster than Babel) esbuild: { - jsx: "automatic", - jsxImportSource: "preact", + jsx: 'automatic', + jsxImportSource: 'preact', jsxDev: isDev }, css: { - transformer: "lightningcss", + transformer: 'lightningcss', devSourcemap: isDev }, resolve: { @@ -101,7 +79,7 @@ export default defineConfig(() => ({ }, build: { target: "esnext", - outDir: "./dist", + outDir: './dist', emptyOutDir: true, reportCompressedSize: true, sourcemap: false, From 7646d8be0727791139f62816d8141520863dbb62 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 19:37:22 +0200 Subject: [PATCH 15/83] client: update the contributor list via a script, persist the list into the repo --- contributors.json | 117 ++++++++++++++++++ package.json | 3 +- .../update-contributor-list.ts | 22 +++- 3 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 contributors.json rename apps/client/src/services/contributors_list.ts => scripts/update-contributor-list.ts (77%) diff --git a/contributors.json b/contributors.json new file mode 100644 index 0000000000..38350f4c5d --- /dev/null +++ b/contributors.json @@ -0,0 +1,117 @@ +{ + "⚠️": "NOTE: this is an auto-generated list. Do not modify it.", + "contributors": [ + { + "name": "eliandoran", + "url": "https://github.com/eliandoran" + }, + { + "name": "zadam", + "url": "https://github.com/zadam" + }, + { + "name": "adoriandoran", + "url": "https://github.com/adoriandoran" + }, + { + "name": "pano9000", + "url": "https://github.com/pano9000" + }, + { + "name": "perfectra1n", + "url": "https://github.com/perfectra1n" + }, + { + "name": "JYC333", + "url": "https://github.com/JYC333" + }, + { + "name": "SiriusXT", + "url": "https://github.com/SiriusXT" + }, + { + "name": "tony", + "url": "https://github.com/tony" + }, + { + "name": "Nriver", + "url": "https://github.com/Nriver" + }, + { + "name": "francistw", + "url": "https://github.com/francistw" + }, + { + "name": "isaul32", + "url": "https://github.com/isaul32" + }, + { + "name": "thfrei", + "url": "https://github.com/thfrei" + }, + { + "name": "nathancahill", + "url": "https://github.com/nathancahill" + }, + { + "name": "contributor", + "url": "https://github.com/contributor" + }, + { + "name": "FliegendeWurst", + "url": "https://github.com/FliegendeWurst" + }, + { + "name": "hasecilu", + "url": "https://github.com/hasecilu" + }, + { + "name": "Meinzzzz", + "url": "https://github.com/Meinzzzz" + }, + { + "name": "Sarah-Hussein", + "url": "https://github.com/Sarah-Hussein" + }, + { + "name": "zerebos", + "url": "https://github.com/zerebos" + }, + { + "name": "meichthys", + "url": "https://github.com/meichthys" + }, + { + "name": "questamor", + "url": "https://github.com/questamor" + }, + { + "name": "SukantGujar", + "url": "https://github.com/SukantGujar" + }, + { + "name": "soulsands", + "url": "https://github.com/soulsands" + }, + { + "name": "noobhjy", + "url": "https://github.com/noobhjy" + }, + { + "name": "laurent22", + "url": "https://github.com/laurent22" + }, + { + "name": "mlewand", + "url": "https://github.com/mlewand" + }, + { + "name": "lzinga", + "url": "https://github.com/lzinga" + }, + { + "name": "ytrkptl", + "url": "https://github.com/ytrkptl" + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index daca67fc3f..fa84898edd 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "dev:linter-check": "cross-env NODE_OPTIONS=--max_old_space_size=4096 eslint .", "dev:linter-fix": "cross-env NODE_OPTIONS=--max_old_space_size=4096 eslint . --fix", "postinstall": "tsx scripts/electron-rebuild.mts && pnpm prepare", - "prepare": "pnpm run --filter pdfjs-viewer --filter share-theme build && pnpm run --filter web-clipper postinstall" + "prepare": "pnpm run --filter pdfjs-viewer --filter share-theme build && pnpm run --filter web-clipper postinstall", + "update-contributor-list": "tsx ./scripts/update-contributor-list.ts" }, "private": true, "devDependencies": { diff --git a/apps/client/src/services/contributors_list.ts b/scripts/update-contributor-list.ts similarity index 77% rename from apps/client/src/services/contributors_list.ts rename to scripts/update-contributor-list.ts index 902d0538ca..e41d1bdf8a 100644 --- a/apps/client/src/services/contributors_list.ts +++ b/scripts/update-contributor-list.ts @@ -1,3 +1,5 @@ +import { writeFileSync } from "fs"; + export interface ContributorList { contributors: Contributor[]; } @@ -14,11 +16,25 @@ const PINNED_CONTRIBUTORS = ["eliandoran", "zadam"]; // Bots marked as users on the GitHub profile info to exclude from the listing const BOTS = ["weblate"]; -export default async function getContributors() { +async function main() { + console.log("Retrieving the contributor list..."); + + let jsonData: any = {}; + try { + jsonData = await getContributors(); + } catch (ex) { + console.error(ex); + } + + writeFileSync("contributors.json", JSON.stringify(jsonData, null, 2)); +} + +async function getContributors() { const response = await fetch("https://api.github.com/repos/TriliumNext/Trilium/contributors"); if (response.ok) { return { + "⚠️": "NOTE: this is an auto-generated list. Do not modify it.", contributors: getList(await response.json()) } as ContributorList } else { @@ -49,4 +65,6 @@ function contributorOrderer(a, b) { // Within each group, sort by contributions return b.contributions - a.contributions; -} \ No newline at end of file +} + +main(); \ No newline at end of file From e7adf08854c74f3c5d79b1fb2b826207bff153f8 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 19:55:46 +0200 Subject: [PATCH 16/83] scripts/update contributor list: add role for pinned contributors --- contributors.json | 2 ++ scripts/update-contributor-list.ts | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/contributors.json b/contributors.json index 38350f4c5d..eb91307d48 100644 --- a/contributors.json +++ b/contributors.json @@ -3,10 +3,12 @@ "contributors": [ { "name": "eliandoran", + "role": "lead-dev", "url": "https://github.com/eliandoran" }, { "name": "zadam", + "role": "original-dev", "url": "https://github.com/zadam" }, { diff --git a/scripts/update-contributor-list.ts b/scripts/update-contributor-list.ts index e41d1bdf8a..1f134f12eb 100644 --- a/scripts/update-contributor-list.ts +++ b/scripts/update-contributor-list.ts @@ -7,11 +7,15 @@ export interface ContributorList { export interface Contributor { name: string; url: string; + role?: "lead-dev" | "original-dev"; } // Keep honorific contributors at top of the list, even if their commit count // is exceeded by another users. -const PINNED_CONTRIBUTORS = ["eliandoran", "zadam"]; +const PINNED_CONTRIBUTORS = { + "eliandoran": "lead-dev", + "zadam": "original-dev" +}; // Bots marked as users on the GitHub profile info to exclude from the listing const BOTS = ["weblate"]; @@ -50,13 +54,14 @@ function getList(contributorInfo: any[]) { .sort(contributorOrderer) .map((c) => {return { name: c.login, + role: (c.login in PINNED_CONTRIBUTORS) ? PINNED_CONTRIBUTORS[c.login]: undefined, url: c.html_url } as Contributor}); } function contributorOrderer(a, b) { - const isAPinned = PINNED_CONTRIBUTORS.includes(a.login); - const isBPinned = PINNED_CONTRIBUTORS.includes(b.login); + const isAPinned = (a.login in PINNED_CONTRIBUTORS); + const isBPinned = (b.login in PINNED_CONTRIBUTORS); // Pinned contributors come first if (isAPinned !== isBPinned) { From f00e051e758fca4bdf99cd8b693ad829c0423fac Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 20:05:47 +0200 Subject: [PATCH 17/83] scripts/update contributor list: add completion feedback --- scripts/update-contributor-list.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/update-contributor-list.ts b/scripts/update-contributor-list.ts index 1f134f12eb..e8dcbcf515 100644 --- a/scripts/update-contributor-list.ts +++ b/scripts/update-contributor-list.ts @@ -28,9 +28,11 @@ async function main() { jsonData = await getContributors(); } catch (ex) { console.error(ex); + return; } writeFileSync("contributors.json", JSON.stringify(jsonData, null, 2)); + console.log("Done."); } async function getContributors() { From 015e50cdb8fd8700e25e826e26fe8abe23adbe73 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 22:11:35 +0200 Subject: [PATCH 18/83] scripts/update contributor list: refactor --- packages/commons/src/index.ts | 3 ++- packages/commons/src/lib/shared_types.ts | 9 +++++++++ scripts/update-contributor-list.ts | 10 +--------- 3 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 packages/commons/src/lib/shared_types.ts diff --git a/packages/commons/src/index.ts b/packages/commons/src/index.ts index b208bfd3b6..ab0a71677a 100644 --- a/packages/commons/src/index.ts +++ b/packages/commons/src/index.ts @@ -8,6 +8,7 @@ export * from "./lib/mime_type.js"; export * from "./lib/bulk_actions.js"; export * from "./lib/server_api.js"; export * from "./lib/shared_constants.js"; +export * from "./lib/shared_types.js"; export * from "./lib/ws_api.js"; export * from "./lib/attribute_names.js"; export * from "./lib/utils.js"; @@ -15,4 +16,4 @@ export * from "./lib/dayjs.js"; export * from "./lib/notes.js"; export * from "./lib/week_utils.js"; export { default as BUILTIN_ATTRIBUTES } from "./lib/builtin_attributes.js"; -export * from "./lib/spreadsheet/render_to_html.js"; +export * from "./lib/spreadsheet/render_to_html.js"; \ No newline at end of file diff --git a/packages/commons/src/lib/shared_types.ts b/packages/commons/src/lib/shared_types.ts new file mode 100644 index 0000000000..571506d2d9 --- /dev/null +++ b/packages/commons/src/lib/shared_types.ts @@ -0,0 +1,9 @@ +export interface ContributorList { + contributors: Contributor[]; +} + +export interface Contributor { + name: string; + url: string; + role?: "lead-dev" | "original-dev"; +} \ No newline at end of file diff --git a/scripts/update-contributor-list.ts b/scripts/update-contributor-list.ts index e8dcbcf515..56f6e444b4 100644 --- a/scripts/update-contributor-list.ts +++ b/scripts/update-contributor-list.ts @@ -1,14 +1,6 @@ import { writeFileSync } from "fs"; -export interface ContributorList { - contributors: Contributor[]; -} - -export interface Contributor { - name: string; - url: string; - role?: "lead-dev" | "original-dev"; -} +import {Contributor, ContributorList} from "../packages/commons/"; // Keep honorific contributors at top of the list, even if their commit count // is exceeded by another users. From b32dd949d738e103d2e999968a888f85f4d34c9d Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 22 Mar 2026 22:35:59 +0200 Subject: [PATCH 19/83] scripts/update contributor list: add full name support for pinned contributors --- contributors.json | 10 ++++++---- packages/commons/src/lib/shared_types.ts | 1 + scripts/update-contributor-list.ts | 23 +++++++++++++++-------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/contributors.json b/contributors.json index eb91307d48..6174022b78 100644 --- a/contributors.json +++ b/contributors.json @@ -3,13 +3,15 @@ "contributors": [ { "name": "eliandoran", - "role": "lead-dev", - "url": "https://github.com/eliandoran" + "url": "https://github.com/eliandoran", + "fullName": "Elian Doran", + "role": "lead-dev" }, { "name": "zadam", - "role": "original-dev", - "url": "https://github.com/zadam" + "url": "https://github.com/zadam", + "fullName": "Zadam", + "role": "original-dev" }, { "name": "adoriandoran", diff --git a/packages/commons/src/lib/shared_types.ts b/packages/commons/src/lib/shared_types.ts index 571506d2d9..17b701eca2 100644 --- a/packages/commons/src/lib/shared_types.ts +++ b/packages/commons/src/lib/shared_types.ts @@ -4,6 +4,7 @@ export interface ContributorList { export interface Contributor { name: string; + fullName?: string; url: string; role?: "lead-dev" | "original-dev"; } \ No newline at end of file diff --git a/scripts/update-contributor-list.ts b/scripts/update-contributor-list.ts index 56f6e444b4..6c8e9ea1fa 100644 --- a/scripts/update-contributor-list.ts +++ b/scripts/update-contributor-list.ts @@ -4,9 +4,9 @@ import {Contributor, ContributorList} from "../packages/commons/"; // Keep honorific contributors at top of the list, even if their commit count // is exceeded by another users. -const PINNED_CONTRIBUTORS = { - "eliandoran": "lead-dev", - "zadam": "original-dev" +const PINNED_CONTRIBUTORS: Record> = { + "eliandoran": {fullName: "Elian Doran", role: "lead-dev"}, + "zadam": {fullName: "Zadam", role: "original-dev"} }; // Bots marked as users on the GitHub profile info to exclude from the listing @@ -46,11 +46,18 @@ function getList(contributorInfo: any[]) { .filter((c) => c.type === "User" && !BOTS.includes(c.login)) // Sort by the commit count. Honorific contributors are always first. .sort(contributorOrderer) - .map((c) => {return { - name: c.login, - role: (c.login in PINNED_CONTRIBUTORS) ? PINNED_CONTRIBUTORS[c.login]: undefined, - url: c.html_url - } as Contributor}); + .map((c) => { + let result = { + name: c.login, + url: c.html_url + } as Contributor; + + if (c.login in PINNED_CONTRIBUTORS) { + result = {...result, ...PINNED_CONTRIBUTORS[c.login]}; + } + + return result; + }); } function contributorOrderer(a, b) { From a7a1e5c48059476853f45a0e1fa7e951ec0d32bc Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Mon, 23 Mar 2026 08:53:52 +0200 Subject: [PATCH 20/83] scripts/update contributor list: optimize --- contributors.json | 4 ++-- scripts/update-contributor-list.ts | 14 ++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/contributors.json b/contributors.json index 6174022b78..6add5804ed 100644 --- a/contributors.json +++ b/contributors.json @@ -3,14 +3,14 @@ "contributors": [ { "name": "eliandoran", - "url": "https://github.com/eliandoran", "fullName": "Elian Doran", + "url": "https://github.com/eliandoran", "role": "lead-dev" }, { "name": "zadam", - "url": "https://github.com/zadam", "fullName": "Zadam", + "url": "https://github.com/zadam", "role": "original-dev" }, { diff --git a/scripts/update-contributor-list.ts b/scripts/update-contributor-list.ts index 6c8e9ea1fa..2070575fe4 100644 --- a/scripts/update-contributor-list.ts +++ b/scripts/update-contributor-list.ts @@ -47,16 +47,14 @@ function getList(contributorInfo: any[]) { // Sort by the commit count. Honorific contributors are always first. .sort(contributorOrderer) .map((c) => { - let result = { + let pinnedInfo = PINNED_CONTRIBUTORS[c.login]; + + return { name: c.login, - url: c.html_url + fullName: pinnedInfo?.fullName, + url: c.html_url, + role: pinnedInfo?.role } as Contributor; - - if (c.login in PINNED_CONTRIBUTORS) { - result = {...result, ...PINNED_CONTRIBUTORS[c.login]}; - } - - return result; }); } From db98884ae4e554e856e34d6183286da896c6912c Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Mon, 23 Mar 2026 09:00:30 +0200 Subject: [PATCH 21/83] scripts/update contributor list: refactor --- scripts/update-contributor-list.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/scripts/update-contributor-list.ts b/scripts/update-contributor-list.ts index 2070575fe4..fcbf415530 100644 --- a/scripts/update-contributor-list.ts +++ b/scripts/update-contributor-list.ts @@ -1,7 +1,6 @@ +import { Contributor, ContributorList } from "../packages/commons/"; import { writeFileSync } from "fs"; -import {Contributor, ContributorList} from "../packages/commons/"; - // Keep honorific contributors at top of the list, even if their commit count // is exceeded by another users. const PINNED_CONTRIBUTORS: Record> = { @@ -10,37 +9,39 @@ const PINNED_CONTRIBUTORS: Record }; // Bots marked as users on the GitHub profile info to exclude from the listing -const BOTS = ["weblate"]; +const BOTS = [ + "weblate" +]; async function main() { console.log("Retrieving the contributor list..."); - let jsonData: any = {}; + let data: any = {}; try { - jsonData = await getContributors(); + data = await fetchContributors(); } catch (ex) { console.error(ex); return; } - writeFileSync("contributors.json", JSON.stringify(jsonData, null, 2)); + writeFileSync("contributors.json", JSON.stringify(data, null, 4)); console.log("Done."); } -async function getContributors() { +async function fetchContributors() { const response = await fetch("https://api.github.com/repos/TriliumNext/Trilium/contributors"); if (response.ok) { return { "⚠️": "NOTE: this is an auto-generated list. Do not modify it.", - contributors: getList(await response.json()) + contributors: processContributorList(await response.json()) } as ContributorList } else { throw new Error(`Unable to request the contributor list from GitHub. Reason: ${response.statusText}`); } } -function getList(contributorInfo: any[]) { +function processContributorList(contributorInfo: any[]) { return contributorInfo // Filter out bots .filter((c) => c.type === "User" && !BOTS.includes(c.login)) From f6e92c411e68e42bc11a112b398fe8c23607066b Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Mon, 23 Mar 2026 09:38:23 +0200 Subject: [PATCH 22/83] client/about dialog: show the contributor list --- .../src/translations/en/translation.json | 4 +++ apps/client/src/widgets/dialogs/about.tsx | 30 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index e1271388b3..8f9e559e30 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -4,6 +4,10 @@ "version": "app: {{appVersion}}, database: {{dbVersion}}, sync protocol: {{syncVersion}}", "build_info": "{{buildDate}}, revision: ", "contributors_label": "Contributors:", + "contributor_roles": { + "lead-dev": "lead developer", + "original-dev": "original developer" + }, "data_directory": "Data directory:", "donate": "Donate" }, diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index d47e8167a4..1978e33eec 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -6,7 +6,7 @@ import utils from "../../services/utils.js"; import openService from "../../services/open.js"; import { useState } from "preact/hooks"; import type { CSSProperties } from "preact/compat"; -import type { AppInfo } from "@triliumnext/commons"; +import type { AppInfo, Contributor, ContributorList } from "@triliumnext/commons"; import { useTriliumEvent } from "../react/hooks.jsx"; import { Card, CardSection } from "../react/Card.js"; import "./about.css"; @@ -15,6 +15,7 @@ import type React from "react"; import icon from "../../assets/icon.svg"; import iconAlt from "../../assets/icon-alt.svg"; import { useCallback, useEffect } from "react"; +import contributors from "../../../../../contributors.json"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); @@ -76,6 +77,9 @@ export default function AboutDialog() {
{t("about.contributors_label")}
+
+ +
{t("about.data_directory")}
@@ -112,3 +116,27 @@ function DirectoryLink({ directory, style }: { directory: string, style?: CSSPro return {directory}; } } + +function Contributors(params: {data: ContributorList}) { + return params.data.contributors.map((c, index, array) => { + return <> + + + {/* Add a comma between items */} + {(index < array.length - 1) ? ", " : undefined} + + }); +} + +function ContributorListItem({data}: {data: Contributor}) { + let roleString = ""; + if (data.role) { + roleString = t(`about.contributor_roles.${data.role}`); + } + + return <> + {data.fullName ?? data.name} + + {roleString &&  ({roleString})} + +} \ No newline at end of file From 129f1ccd8d9d6887fec1f88fc5fbfe7636ab5a92 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Mon, 23 Mar 2026 09:52:04 +0200 Subject: [PATCH 23/83] client/about dialog: add a full list link, improve layout --- apps/client/src/translations/en/translation.json | 1 + apps/client/src/widgets/dialogs/about.css | 6 ++++++ apps/client/src/widgets/dialogs/about.tsx | 7 +++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 8f9e559e30..a7aa1777f7 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -8,6 +8,7 @@ "lead-dev": "lead developer", "original-dev": "original developer" }, + "contributor_full_list": "See the full list", "data_directory": "Data directory:", "donate": "Donate" }, diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index d6113c9bc1..d17e4f6d07 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -18,6 +18,12 @@ } } + .contributor-list { + a, span { + white-space: nowrap; + } + } + footer { display: flex; justify-content: center; diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 1978e33eec..498d196cf0 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -77,8 +77,11 @@ export default function AboutDialog() {
{t("about.contributors_label")}
-
+ @@ -123,7 +126,7 @@ function Contributors(params: {data: ContributorList}) { {/* Add a comma between items */} - {(index < array.length - 1) ? ", " : undefined} + {(index < array.length - 1) ? ", " : ". "} }); } From fc6bdb56df75134c5ad30a534eb06d29d4484a10 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Mon, 23 Mar 2026 10:06:53 +0200 Subject: [PATCH 24/83] client/about dialog: refactor --- apps/client/src/widgets/dialogs/about.tsx | 43 +++++++++++++---------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 498d196cf0..9baf417077 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -5,7 +5,6 @@ import server from "../../services/server.js"; import utils from "../../services/utils.js"; import openService from "../../services/open.js"; import { useState } from "preact/hooks"; -import type { CSSProperties } from "preact/compat"; import type { AppInfo, Contributor, ContributorList } from "@triliumnext/commons"; import { useTriliumEvent } from "../react/hooks.jsx"; import { Card, CardSection } from "../react/Card.js"; @@ -36,13 +35,13 @@ export default function AboutDialog() { }, [appInfo]) return ( - setShown(false)} >
+
{t("about.contributors_label")}
@@ -84,6 +80,7 @@ export default function AboutDialog() {
+
{t("about.data_directory")}
@@ -107,17 +104,12 @@ export default function AboutDialog() { ); } -function DirectoryLink({ directory, style }: { directory: string, style?: CSSProperties }) { - if (utils.isElectron()) { - const onClick = (e: MouseEvent) => { - e.preventDefault(); - openService.openDirectory(directory); - }; - - return {directory} - } else { - return {directory}; - } +function revisionLink(appInfo: AppInfo | null) { + return <> + {appInfo?.buildRevision && + {appInfo.buildRevision.substring(0, 7)} + } + as React.ReactElement; } function Contributors(params: {data: ContributorList}) { @@ -142,4 +134,17 @@ function ContributorListItem({data}: {data: Contributor}) { {roleString &&  ({roleString})} +} + +function DirectoryLink({ directory }: { directory: string}) { + if (utils.isElectron()) { + const onClick = (e: MouseEvent) => { + e.preventDefault(); + openService.openDirectory(directory); + }; + + return {directory} + } else { + return {directory}; + } } \ No newline at end of file From dfd68ca8a30b0e6ccb24ac766bb50e0c0399626f Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Mon, 23 Mar 2026 10:07:47 +0200 Subject: [PATCH 25/83] scripts/update contributor list: update indentation size --- contributors.json | 238 +++++++++++++++++++++++----------------------- 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/contributors.json b/contributors.json index 6add5804ed..59057dfa33 100644 --- a/contributors.json +++ b/contributors.json @@ -1,121 +1,121 @@ { - "⚠️": "NOTE: this is an auto-generated list. Do not modify it.", - "contributors": [ - { - "name": "eliandoran", - "fullName": "Elian Doran", - "url": "https://github.com/eliandoran", - "role": "lead-dev" - }, - { - "name": "zadam", - "fullName": "Zadam", - "url": "https://github.com/zadam", - "role": "original-dev" - }, - { - "name": "adoriandoran", - "url": "https://github.com/adoriandoran" - }, - { - "name": "pano9000", - "url": "https://github.com/pano9000" - }, - { - "name": "perfectra1n", - "url": "https://github.com/perfectra1n" - }, - { - "name": "JYC333", - "url": "https://github.com/JYC333" - }, - { - "name": "SiriusXT", - "url": "https://github.com/SiriusXT" - }, - { - "name": "tony", - "url": "https://github.com/tony" - }, - { - "name": "Nriver", - "url": "https://github.com/Nriver" - }, - { - "name": "francistw", - "url": "https://github.com/francistw" - }, - { - "name": "isaul32", - "url": "https://github.com/isaul32" - }, - { - "name": "thfrei", - "url": "https://github.com/thfrei" - }, - { - "name": "nathancahill", - "url": "https://github.com/nathancahill" - }, - { - "name": "contributor", - "url": "https://github.com/contributor" - }, - { - "name": "FliegendeWurst", - "url": "https://github.com/FliegendeWurst" - }, - { - "name": "hasecilu", - "url": "https://github.com/hasecilu" - }, - { - "name": "Meinzzzz", - "url": "https://github.com/Meinzzzz" - }, - { - "name": "Sarah-Hussein", - "url": "https://github.com/Sarah-Hussein" - }, - { - "name": "zerebos", - "url": "https://github.com/zerebos" - }, - { - "name": "meichthys", - "url": "https://github.com/meichthys" - }, - { - "name": "questamor", - "url": "https://github.com/questamor" - }, - { - "name": "SukantGujar", - "url": "https://github.com/SukantGujar" - }, - { - "name": "soulsands", - "url": "https://github.com/soulsands" - }, - { - "name": "noobhjy", - "url": "https://github.com/noobhjy" - }, - { - "name": "laurent22", - "url": "https://github.com/laurent22" - }, - { - "name": "mlewand", - "url": "https://github.com/mlewand" - }, - { - "name": "lzinga", - "url": "https://github.com/lzinga" - }, - { - "name": "ytrkptl", - "url": "https://github.com/ytrkptl" - } - ] + "⚠️": "NOTE: this is an auto-generated list. Do not modify it.", + "contributors": [ + { + "name": "eliandoran", + "fullName": "Elian Doran", + "url": "https://github.com/eliandoran", + "role": "lead-dev" + }, + { + "name": "zadam", + "fullName": "Zadam", + "url": "https://github.com/zadam", + "role": "original-dev" + }, + { + "name": "adoriandoran", + "url": "https://github.com/adoriandoran" + }, + { + "name": "pano9000", + "url": "https://github.com/pano9000" + }, + { + "name": "perfectra1n", + "url": "https://github.com/perfectra1n" + }, + { + "name": "JYC333", + "url": "https://github.com/JYC333" + }, + { + "name": "SiriusXT", + "url": "https://github.com/SiriusXT" + }, + { + "name": "tony", + "url": "https://github.com/tony" + }, + { + "name": "Nriver", + "url": "https://github.com/Nriver" + }, + { + "name": "francistw", + "url": "https://github.com/francistw" + }, + { + "name": "isaul32", + "url": "https://github.com/isaul32" + }, + { + "name": "thfrei", + "url": "https://github.com/thfrei" + }, + { + "name": "nathancahill", + "url": "https://github.com/nathancahill" + }, + { + "name": "contributor", + "url": "https://github.com/contributor" + }, + { + "name": "FliegendeWurst", + "url": "https://github.com/FliegendeWurst" + }, + { + "name": "hasecilu", + "url": "https://github.com/hasecilu" + }, + { + "name": "Meinzzzz", + "url": "https://github.com/Meinzzzz" + }, + { + "name": "Sarah-Hussein", + "url": "https://github.com/Sarah-Hussein" + }, + { + "name": "zerebos", + "url": "https://github.com/zerebos" + }, + { + "name": "meichthys", + "url": "https://github.com/meichthys" + }, + { + "name": "questamor", + "url": "https://github.com/questamor" + }, + { + "name": "SukantGujar", + "url": "https://github.com/SukantGujar" + }, + { + "name": "soulsands", + "url": "https://github.com/soulsands" + }, + { + "name": "noobhjy", + "url": "https://github.com/noobhjy" + }, + { + "name": "laurent22", + "url": "https://github.com/laurent22" + }, + { + "name": "mlewand", + "url": "https://github.com/mlewand" + }, + { + "name": "lzinga", + "url": "https://github.com/lzinga" + }, + { + "name": "ytrkptl", + "url": "https://github.com/ytrkptl" + } + ] } \ No newline at end of file From 97e52e53bf46224a2b41bf141d6b1dee1edbd8a6 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Mon, 23 Mar 2026 10:34:03 +0200 Subject: [PATCH 26/83] Update scripts/update-contributor-list.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- scripts/update-contributor-list.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-contributor-list.ts b/scripts/update-contributor-list.ts index fcbf415530..d6478c2da4 100644 --- a/scripts/update-contributor-list.ts +++ b/scripts/update-contributor-list.ts @@ -41,7 +41,7 @@ async function fetchContributors() { } } -function processContributorList(contributorInfo: any[]) { +function processContributorList(contributorInfo: GithubContributor[]) { return contributorInfo // Filter out bots .filter((c) => c.type === "User" && !BOTS.includes(c.login)) From 35023243891e635afd1b9a29a0f67f5486a0e3f9 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Mon, 23 Mar 2026 10:36:32 +0200 Subject: [PATCH 27/83] client/about dialog: add keys to the contributor list --- apps/client/src/widgets/dialogs/about.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 9baf417077..5fd33cb6dc 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -15,6 +15,7 @@ import icon from "../../assets/icon.svg"; import iconAlt from "../../assets/icon-alt.svg"; import { useCallback, useEffect } from "react"; import contributors from "../../../../../contributors.json"; +import { Fragment } from "preact/jsx-runtime"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); @@ -114,12 +115,12 @@ function revisionLink(appInfo: AppInfo | null) { function Contributors(params: {data: ContributorList}) { return params.data.contributors.map((c, index, array) => { - return <> + return {/* Add a comma between items */} {(index < array.length - 1) ? ", " : ". "} - + }); } From 0a0157a1ef22847bc7832c8d16498d851e3bc152 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Mon, 23 Mar 2026 11:41:13 +0200 Subject: [PATCH 28/83] style/about dialog: improve appearance --- apps/client/src/widgets/dialogs/about.css | 28 +++++++++++++++++++++-- apps/client/src/widgets/dialogs/about.tsx | 4 ++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index d17e4f6d07..c761432aa6 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -1,4 +1,11 @@ .about-dialog { + h2 { + all: unset; + font-size: 2em; + font-weight: 300; + letter-spacing: 1pt; + } + .about-dialog-content { display: flex; flex-direction: column; @@ -18,6 +25,11 @@ } } + .build-info { + color: var(--muted-text-color); + font-size: .9em; + } + .contributor-list { a, span { white-space: nowrap; @@ -27,20 +39,32 @@ footer { display: flex; justify-content: center; - gap: 20px; + gap: 10px; margin-bottom: 30px; a { display: flex; flex-direction: column; align-items: center; + padding: 8px 10px; + border-radius: 6px; + font-size: .9rem; + color: var(--main-text-color); + &:hover { + background: var(--icon-button-hover-background); + } + &::after { display: none; } i { - font-size: 32px; + font-size: 28px; + } + + &.donate-link { + color: #E33F3B; } } } diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 5fd33cb6dc..99510bdda3 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -58,7 +58,7 @@ export default function AboutDialog() { dbVersion: appInfo?.dbVersion, syncVersion: appInfo?.syncVersion })} -
+
GitHub - + {t("about.donate")} From 11a46f0f581e5cbeb32fcf6d7fe4a55523d95ca9 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Tue, 24 Mar 2026 18:54:48 +0200 Subject: [PATCH 29/83] client/about dialog: reduce the contributor list length to 10 --- apps/client/src/translations/en/translation.json | 2 +- apps/client/src/widgets/dialogs/about.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index a7aa1777f7..1dd00bd710 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -8,7 +8,7 @@ "lead-dev": "lead developer", "original-dev": "original developer" }, - "contributor_full_list": "See the full list", + "contributor_full_list": "See the entire community", "data_directory": "Data directory:", "donate": "Donate" }, diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 99510bdda3..731707b73d 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -114,7 +114,7 @@ function revisionLink(appInfo: AppInfo | null) { } function Contributors(params: {data: ContributorList}) { - return params.data.contributors.map((c, index, array) => { + return params.data.contributors.splice(0, 10).map((c, index, array) => { return From 1c74a019ab93975b0052979ca6910d02bbde0eff Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Tue, 24 Mar 2026 19:32:14 +0200 Subject: [PATCH 30/83] style/about dialog: create a card-like table style --- apps/client/src/widgets/dialogs/about.css | 47 ++++++++++++++++++----- apps/client/src/widgets/dialogs/about.tsx | 34 ++++++++-------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index c761432aa6..cf9ac12b68 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -1,4 +1,6 @@ .about-dialog { + --bs-modal-width: 650px; + h2 { all: unset; font-size: 2em; @@ -12,17 +14,10 @@ align-items: center; } - .property-sheet-card { + .property-sheet-table { margin-block: 30px; font-size: .85em; - - .tn-card-section { - display: flex; - - > :first-child { - width: 100px; - } - } + margin-inline: 20px; } .build-info { @@ -68,4 +63,38 @@ } } } +} + +/* TODO: move to global styles */ +.property-sheet-table { + border-spacing: 0 2px; + border-collapse: separate; + + tr { + --_br: 8px; + + background: var(--card-background-color); + + &:first-child { + clip-path: inset(0 round var(--_br) var(--_br) 0 0); + } + + &:last-child { + clip-path: inset(0 round 0 0 var(--_br) var(--_br)); + } + } + + td { + padding: 8px 16px; + vertical-align: top; + + &:first-child { + white-space: nowrap; + color: var(--muted-text-color); + } + + &:last-child { + width: 100%; + } + } } \ No newline at end of file diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 731707b73d..ea13ec8b74 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -49,10 +49,10 @@ export default function AboutDialog() { triliumnotes.org - - -
{t("about.version_label")}
-
+ + + + + - -
{t("about.contributors_label")}
-
+
+ + + - -
{t("about.data_directory")}
-
+
+ + + +
{t("about.version_label")} {t("about.version", { appVersion: appInfo?.appVersion, dbVersion: appInfo?.dbVersion, @@ -69,26 +69,26 @@ export default function AboutDialog() { }} /> - - +
{t("about.contributors_label")} {t("about.contributor_full_list")} - - +
{t("about.data_directory")} {appInfo?.dataDirectory && ()} - - - +
@@ -91,27 +92,37 @@ export default function AboutDialog() {
); } -function revisionLink(appInfo: AppInfo | null) { +function RevisionLink(appInfo: AppInfo | null) { return <> {appInfo?.buildRevision && {appInfo.buildRevision.substring(0, 7)} @@ -119,8 +130,15 @@ function revisionLink(appInfo: AppInfo | null) { as React.ReactElement; } -function Contributors(params: {data: ContributorList}) { - return params.data.contributors.slice(0, 10).map((c, index, array) => { +function FooterLink(props: {children: ComponentChildren, text: string, url: string, tooltip: string, className?: string}) { + return + {props.children} + {props.text} + +} + +function Contributors({data}: {data: ContributorList}) { + return data.contributors.slice(0, 10).map((c, index, array) => { return From a6b1af6a16ce512fe22f25b75e9a3f8939dc3f57 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Tue, 24 Mar 2026 23:57:44 +0200 Subject: [PATCH 36/83] style/about dialog: improve appearance --- apps/client/src/widgets/dialogs/about.css | 2 +- apps/client/src/widgets/dialogs/about.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index eb33ef0800..ef98b26f78 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -105,7 +105,7 @@ } td { - padding: 8px 16px; + padding: 10px 16px; vertical-align: top; &:first-child { diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index a8ae1781c2..b70f4ef7f5 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -131,7 +131,7 @@ function RevisionLink(appInfo: AppInfo | null) { } function FooterLink(props: {children: ComponentChildren, text: string, url: string, tooltip: string, className?: string}) { - return + return {props.children} {props.text} From d370ee2d99083e9159bf9a194407a95d9440a8fd Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Wed, 25 Mar 2026 00:28:38 +0200 Subject: [PATCH 37/83] style/about dialog: select the icon using CSS class names --- apps/client/src/assets/icon-classic.svg | 1 + .../assets/{icon-alt.svg => icon-nightly.svg} | 0 apps/client/src/widgets/dialogs/about.css | 18 ++++++++++++++++++ apps/client/src/widgets/dialogs/about.tsx | 10 ++++++++-- 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 apps/client/src/assets/icon-classic.svg rename apps/client/src/assets/{icon-alt.svg => icon-nightly.svg} (100%) diff --git a/apps/client/src/assets/icon-classic.svg b/apps/client/src/assets/icon-classic.svg new file mode 100644 index 0000000000..0fbc903d8a --- /dev/null +++ b/apps/client/src/assets/icon-classic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/client/src/assets/icon-alt.svg b/apps/client/src/assets/icon-nightly.svg similarity index 100% rename from apps/client/src/assets/icon-alt.svg rename to apps/client/src/assets/icon-nightly.svg diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index ef98b26f78..20182f1104 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -10,6 +10,24 @@ --bs-modal-width: 680px; + .icon { + width: 160px; + height: 160px; + + &.icon-default { + background-image: url(../../assets/icon.svg); + } + + &.icon-nightly { + background-image: url(../../assets/icon-nightly.svg); + } + + &.icon-classic { + mask-image: url(../../assets/icon-classic.svg); + background-color: var(--muted-text-color); + } + } + h2 { all: unset; font-size: 2em; diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index b70f4ef7f5..b45cd19905 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -16,11 +16,13 @@ import { useCallback, useEffect } from "react"; import contributors from "../../../../../contributors.json"; import { Fragment } from "preact/jsx-runtime"; import { ComponentChildren } from "preact"; +import clsx from "clsx"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); const [shown, setShown] = useState(false); const [isNightly, setNightly] = useState(false); + const [iconClassName, setIconClassName] = useState("icon-default"); const onLoad = useCallback(async () => { if (!appInfo) { @@ -33,17 +35,21 @@ export default function AboutDialog() { useEffect(() => { setNightly(!!appInfo?.appVersion.includes("test")); + if (isNightly) { + setIconClassName("icon-nightly"); + } }, [appInfo]) return ( setShown(false)} >
- + +

Trilium Notes {isNightly && Nightly}

triliumnotes.org From a81dae2ad005cdd41fb37dc6f49a98e9eccebaaf Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Wed, 25 Mar 2026 01:01:16 +0200 Subject: [PATCH 38/83] client/about dialog: allow toggling to the classic icon --- apps/client/src/widgets/dialogs/about.tsx | 30 +++++++++++++++++------ 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index b45cd19905..f2df3dad83 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -10,8 +10,6 @@ import { useTriliumEvent } from "../react/hooks.jsx"; import "./about.css"; import { Trans } from "react-i18next"; import type React from "react"; -import icon from "../../assets/icon.svg"; -import iconAlt from "../../assets/icon-alt.svg"; import { useCallback, useEffect } from "react"; import contributors from "../../../../../contributors.json"; import { Fragment } from "preact/jsx-runtime"; @@ -23,6 +21,8 @@ export default function AboutDialog() { const [shown, setShown] = useState(false); const [isNightly, setNightly] = useState(false); const [iconClassName, setIconClassName] = useState("icon-default"); + const [altIconClassName, setAltIconClassName] = useState(null); + const onLoad = useCallback(async () => { if (!appInfo) { @@ -49,7 +49,7 @@ export default function AboutDialog() { >
-
+

Trilium Notes {isNightly && Nightly}

triliumnotes.org @@ -81,7 +81,11 @@ export default function AboutDialog() { {t("about.contributors_label")} - + {setAltIconClassName((isHovering && contributor.role === "original-dev") ? "icon-classic" : null)}} + /> + {t("about.contributor_full_list")} @@ -143,10 +147,12 @@ function FooterLink(props: {children: ComponentChildren, text: string, url: stri } -function Contributors({data}: {data: ContributorList}) { +type HoverCallback = (contributor: Contributor, isHovering: boolean) => void; + +function Contributors({data, onHover}: {data: ContributorList, onHover?: HoverCallback}) { return data.contributors.slice(0, 10).map((c, index, array) => { return - + {/* Add a comma between items */} {(index < array.length - 1) ? ", " : ". "} @@ -154,14 +160,22 @@ function Contributors({data}: {data: ContributorList}) { }); } -function ContributorListItem({data}: {data: Contributor}) { + +function ContributorListItem({data, onHover}: {data: Contributor, onHover?: HoverCallback}) { let roleString = ""; if (data.role) { roleString = t(`about.contributor_roles.${data.role}`); } return <> - {data.fullName ?? data.name} + onHover?.(data, true)} + onMouseLeave={(e) => onHover?.(data, false)}> + + {data.fullName ?? data.name} + {roleString &&  ({roleString})} From 591b3a121f8c6d23691a8427df5943c0788e2e98 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Wed, 25 Mar 2026 01:41:00 +0200 Subject: [PATCH 39/83] style/about dialog: add icon animation --- apps/client/src/widgets/dialogs/about.css | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 20182f1104..e8a0c8233e 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -22,9 +22,17 @@ background-image: url(../../assets/icon-nightly.svg); } + &.icon-default, + &.icon-nightly { + animation: icon-intro 500ms ease-out; + will-change: opacity, transform; + } + &.icon-classic { mask-image: url(../../assets/icon-classic.svg); background-color: var(--muted-text-color); + animation: icon-classic-intro 300ms ease-in-out; + will-change: opacity, transform; } } @@ -103,6 +111,26 @@ } } +@keyframes icon-intro { + from { + opacity: 0; + transform: scale(.5); + } to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes icon-classic-intro { + from { + opacity: 0; + transform: rotate(50deg) scale(.5); + } to { + opacity: 1; + transform: rotate(0deg) scale(1.25); + } +} + /* TODO: move to global styles */ .property-sheet-table { border-spacing: 0 2px; From 496405d922ed9fd7a8c17e314068fa1ede491e4c Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Wed, 25 Mar 2026 11:19:54 +0200 Subject: [PATCH 40/83] client/about dialog: add custom tooltips --- .../src/translations/en/translation.json | 4 ++ apps/client/src/widgets/dialogs/about.css | 10 +-- apps/client/src/widgets/dialogs/about.tsx | 66 +++++++++++++------ 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index dd658a2c32..2491e37801 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -8,6 +8,10 @@ "lead-dev": "lead developer", "original-dev": "original developer" }, + "role_brief_history": { + "lead-dev": "TODO", + "original-dev": "TODO" + }, "contributor_full_list": "See the entire community", "data_directory": "Data directory:", "github_tooltip": "Report bugs, suggest features, or contribute on GitHub", diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index e8a0c8233e..97c8cc093c 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -14,21 +14,21 @@ width: 160px; height: 160px; - &.icon-default { + &:not([data-alt-icon]) { background-image: url(../../assets/icon.svg); } - &.icon-nightly { + &[data-alt-icon="nightly"] { background-image: url(../../assets/icon-nightly.svg); } - &.icon-default, - &.icon-nightly { + &:not([data-alt-icon]), + &[data-alt-icon="nightly"] { animation: icon-intro 500ms ease-out; will-change: opacity, transform; } - &.icon-classic { + &[data-alt-icon="classic"] { mask-image: url(../../assets/icon-classic.svg); background-color: var(--muted-text-color); animation: icon-classic-intro 300ms ease-in-out; diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index f2df3dad83..456bf94d6c 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -4,41 +4,52 @@ import { formatDateTime } from "../../utils/formatters.js"; import server from "../../services/server.js"; import utils from "../../services/utils.js"; import openService from "../../services/open.js"; -import { useState } from "preact/hooks"; +import { useState, useCallback, useRef } from "preact/hooks"; import type { AppInfo, Contributor, ContributorList } from "@triliumnext/commons"; -import { useTriliumEvent } from "../react/hooks.jsx"; +import { useTooltip, useTriliumEvent } from "../react/hooks.jsx"; import "./about.css"; import { Trans } from "react-i18next"; import type React from "react"; -import { useCallback, useEffect } from "react"; import contributors from "../../../../../contributors.json"; import { Fragment } from "preact/jsx-runtime"; import { ComponentChildren } from "preact"; -import clsx from "clsx"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); const [shown, setShown] = useState(false); const [isNightly, setNightly] = useState(false); - const [iconClassName, setIconClassName] = useState("icon-default"); - const [altIconClassName, setAltIconClassName] = useState(null); - + const iconRef = useRef(null); const onLoad = useCallback(async () => { if (!appInfo) { - setAppInfo(await server.get("app-info")); + const info = await server.get("app-info"); + if (info.appVersion.includes("test")) { + setNightly(true); + setAltIcon("nightly"); + } + setAppInfo(info); } setShown(true); }, []); useTriliumEvent("openAboutDialog", onLoad); - useEffect(() => { - setNightly(!!appInfo?.appVersion.includes("test")); - if (isNightly) { - setIconClassName("icon-nightly"); + const setAltIcon = useCallback((iconId: string | null) => { + /* The alternate icon is set by directly accessing the DOM to prevent the dialog being + * rerendered. A rerender while an element is hovered and displaying a tooltip in the same + * time, will cause the tooltip to break. */ + if (iconId) { + iconRef.current?.setAttribute("data-alt-icon", iconId); + } else { + iconRef.current?.removeAttribute("data-alt-icon"); } - }, [appInfo]) + }, []); + + const onContributorHovered = useCallback((contributor: Contributor, isHovering: boolean) => { + if (contributor.role === "original-dev") { + setAltIcon((isHovering) ? "classic": null); + } + }, []); return (
-
+

Trilium Notes {isNightly && Nightly}

triliumnotes.org @@ -83,7 +94,7 @@ export default function AboutDialog() { {setAltIconClassName((isHovering && contributor.role === "original-dev") ? "icon-classic" : null)}} + onHover={onContributorHovered} /> @@ -141,7 +152,16 @@ function RevisionLink(appInfo: AppInfo | null) { } function FooterLink(props: {children: ComponentChildren, text: string, url: string, tooltip: string, className?: string}) { - return + + const linkRef = useRef(null); + + useTooltip(linkRef, { + title: props.tooltip, + delay: 250, + placement: "bottom" + }) + + return {props.children} {props.text} @@ -162,13 +182,19 @@ function Contributors({data, onHover}: {data: ContributorList, onHover?: HoverCa function ContributorListItem({data, onHover}: {data: Contributor, onHover?: HoverCallback}) { - let roleString = ""; - if (data.role) { - roleString = t(`about.contributor_roles.${data.role}`); - } + const linkRef = useRef(null); + const roleString = (data.role) ? t(`about.contributor_roles.${data.role}`) : ""; + + useTooltip(linkRef, (data.role) ? { + title: t(`about.role_brief_history.${data.role}`), + placement: "bottom", + offset: [0, 10], + delay: 500 + }: {}); return <> onHover?.(data, true)} From 573ab077ac211993a307c79ccf64b2b6d24accb2 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Wed, 25 Mar 2026 11:30:50 +0200 Subject: [PATCH 41/83] client/about dialog: add a delay for contributor link hover event --- apps/client/src/widgets/dialogs/about.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 456bf94d6c..35c95eee47 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -45,9 +45,19 @@ export default function AboutDialog() { } }, []); - const onContributorHovered = useCallback((contributor: Contributor, isHovering: boolean) => { - if (contributor.role === "original-dev") { - setAltIcon((isHovering) ? "classic": null); + const createContributorHoverHandler = useCallback(() => { + let timeoutID; + return (contributor: Contributor, isHovering: boolean) => { + if (contributor.role === "original-dev") { + if (isHovering) { + timeoutID = setTimeout(() => { + setAltIcon("classic"); + }, 500); + } else { + clearTimeout(timeoutID); + setAltIcon(null); + } + } } }, []); @@ -94,7 +104,7 @@ export default function AboutDialog() { From 0386e7dd4f30e7cdb0fd37e9be0da0fac870d884 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Wed, 25 Mar 2026 11:41:50 +0200 Subject: [PATCH 42/83] style/about dialog: add a clue about contributor links with a tooltip --- apps/client/src/widgets/dialogs/about.css | 4 ++++ apps/client/src/widgets/dialogs/about.tsx | 2 ++ 2 files changed, 6 insertions(+) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 97c8cc093c..92f04a9909 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -68,6 +68,10 @@ a, span { white-space: nowrap; } + + a.has-tooltip { + text-decoration: underline dotted var(--main-text-color) !important; + } } footer { diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 35c95eee47..668d8ab907 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -13,6 +13,7 @@ import type React from "react"; import contributors from "../../../../../contributors.json"; import { Fragment } from "preact/jsx-runtime"; import { ComponentChildren } from "preact"; +import clsx from "clsx"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); @@ -205,6 +206,7 @@ function ContributorListItem({data, onHover}: {data: Contributor, onHover?: Hove return <> onHover?.(data, true)} From d4552fa07596acb181c80a5c8c1de6b4c39ecb7b Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Fri, 27 Mar 2026 18:08:36 +0200 Subject: [PATCH 43/83] client/about dialog: refactor --- apps/client/src/widgets/dialogs/about.css | 10 +++--- apps/client/src/widgets/dialogs/about.tsx | 39 +++++++++++------------ 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 92f04a9909..5644431d5d 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -14,21 +14,21 @@ width: 160px; height: 160px; - &:not([data-alt-icon]) { + &[data-icon="default"] { background-image: url(../../assets/icon.svg); } - &[data-alt-icon="nightly"] { + &[data-icon="nightly"] { background-image: url(../../assets/icon-nightly.svg); } - &:not([data-alt-icon]), - &[data-alt-icon="nightly"] { + &[data-icon="default"], + &[data-icon="nightly"] { animation: icon-intro 500ms ease-out; will-change: opacity, transform; } - &[data-alt-icon="classic"] { + &[data-icon="classic"] { mask-image: url(../../assets/icon-classic.svg); background-color: var(--muted-text-color); animation: icon-classic-intro 300ms ease-in-out; diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 668d8ab907..22220b0cfe 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -14,19 +14,22 @@ import contributors from "../../../../../contributors.json"; import { Fragment } from "preact/jsx-runtime"; import { ComponentChildren } from "preact"; import clsx from "clsx"; +import { useMemo } from "react"; +import { memo } from "preact/compat"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); const [shown, setShown] = useState(false); const [isNightly, setNightly] = useState(false); - const iconRef = useRef(null); + const [iconName, setIconName] = useState("default"); + const [altIconName, setAltIconName] = useState(null); const onLoad = useCallback(async () => { if (!appInfo) { const info = await server.get("app-info"); if (info.appVersion.includes("test")) { setNightly(true); - setAltIcon("nightly"); + setIconName("nightly"); } setAppInfo(info); } @@ -35,33 +38,32 @@ export default function AboutDialog() { useTriliumEvent("openAboutDialog", onLoad); - const setAltIcon = useCallback((iconId: string | null) => { - /* The alternate icon is set by directly accessing the DOM to prevent the dialog being - * rerendered. A rerender while an element is hovered and displaying a tooltip in the same - * time, will cause the tooltip to break. */ - if (iconId) { - iconRef.current?.setAttribute("data-alt-icon", iconId); - } else { - iconRef.current?.removeAttribute("data-alt-icon"); - } - }, []); - const createContributorHoverHandler = useCallback(() => { let timeoutID; return (contributor: Contributor, isHovering: boolean) => { if (contributor.role === "original-dev") { if (isHovering) { timeoutID = setTimeout(() => { - setAltIcon("classic"); + setAltIconName("classic"); }, 500); } else { clearTimeout(timeoutID); - setAltIcon(null); + setAltIconName(null); } } } }, []); + /* Cache the contributor list to prevent its rerendering. + * When the icon changes, it triggers a rerender of the dialog. If this happens while an + * element with a tooltip is hovered, its tooltip will break. */ + const CachedContributors = useMemo(() => memo(function CachedContributors() { + return + }), []); + return (
-
+

Trilium Notes {isNightly && Nightly}

triliumnotes.org @@ -103,10 +105,7 @@ export default function AboutDialog() { {t("about.contributors_label")} - + {t("about.contributor_full_list")} From 72cc5cc5ead841a61bc25e6d2813e6cc7496a8e0 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Fri, 27 Mar 2026 18:23:05 +0200 Subject: [PATCH 44/83] style/about dialog: update the donate button link on light theme --- apps/client/src/widgets/dialogs/about.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 5644431d5d..de7a18f1b9 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -1,7 +1,7 @@ .about-dialog { body.light-theme & { - --donate-button-color: #d78382; + --donate-button-color: #e33f3b; } body.dark-theme & { From 355209769f0554438584950143a6c29dab9c85bc Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Fri, 27 Mar 2026 18:30:24 +0200 Subject: [PATCH 45/83] style/tooltips: use higher contrast on the light theme --- apps/client/src/stylesheets/theme-next-light.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/client/src/stylesheets/theme-next-light.css b/apps/client/src/stylesheets/theme-next-light.css index fe604906dc..05211e4e02 100644 --- a/apps/client/src/stylesheets/theme-next-light.css +++ b/apps/client/src/stylesheets/theme-next-light.css @@ -269,9 +269,9 @@ --timeline-connector-active-color: #ddd; --timeline-connector-hover-blend-mode: multiply; - --tooltip-background-color: rgba(255, 255, 255, 0.85); - --tooltip-foreground-color: #000000ba; - --tooltip-shadow-color: rgba(0, 0, 0, 0.15); + --tooltip-background-color: rgba(0, 0, 0, 0.818); + --tooltip-foreground-color: #ffffffeb; + --tooltip-shadow-color: rgba(0, 0, 0, 0.2); --help-background-color: #fffc; --help-card-background: var(--card-background-color); From 87fb568995d8f66602e3f4d563c2ff1b6eb8f1a7 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Fri, 27 Mar 2026 18:42:26 +0200 Subject: [PATCH 46/83] style/about dialog: customize the style of tooltips used for brief history --- apps/client/src/widgets/dialogs/about.css | 4 ++++ apps/client/src/widgets/dialogs/about.tsx | 1 + 2 files changed, 5 insertions(+) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index de7a18f1b9..5c907510e8 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -115,6 +115,10 @@ } } +.about-dialog-brief-history-tooltip { + --main-font-size: .85em; +} + @keyframes icon-intro { from { opacity: 0; diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 22220b0cfe..3555dd18a7 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -197,6 +197,7 @@ function ContributorListItem({data, onHover}: {data: Contributor, onHover?: Hove useTooltip(linkRef, (data.role) ? { title: t(`about.role_brief_history.${data.role}`), + customClass: "about-dialog-brief-history-tooltip", placement: "bottom", offset: [0, 10], delay: 500 From 2432bb11c7781015a12b4330373648a3dfa8a933 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Fri, 27 Mar 2026 19:13:05 +0200 Subject: [PATCH 47/83] style/about dialog: allow long directory paths to be wrapped --- apps/client/src/widgets/dialogs/about.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 3555dd18a7..dba0ed7bb5 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -115,7 +115,7 @@ export default function AboutDialog() { {t("about.data_directory")} - + {appInfo?.dataDirectory && ()} From 601f246bdc13dd2f1742355d593949b5ffe980dd Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Tue, 31 Mar 2026 21:29:32 +0300 Subject: [PATCH 48/83] style/about dialog: move the brief history tooltip from the contributor name link to the role string --- apps/client/src/widgets/dialogs/about.css | 7 +++--- apps/client/src/widgets/dialogs/about.tsx | 28 +++++++++++++---------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 5c907510e8..9b707cb365 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -68,9 +68,10 @@ a, span { white-space: nowrap; } - - a.has-tooltip { - text-decoration: underline dotted var(--main-text-color) !important; + + .contributor-role { + text-decoration: underline dotted var(--main-text-color); + cursor: help; } } diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index dba0ed7bb5..cf0aad263f 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -13,7 +13,6 @@ import type React from "react"; import contributors from "../../../../../contributors.json"; import { Fragment } from "preact/jsx-runtime"; import { ComponentChildren } from "preact"; -import clsx from "clsx"; import { useMemo } from "react"; import { memo } from "preact/compat"; @@ -40,8 +39,8 @@ export default function AboutDialog() { const createContributorHoverHandler = useCallback(() => { let timeoutID; - return (contributor: Contributor, isHovering: boolean) => { - if (contributor.role === "original-dev") { + return (contributor: Contributor, isHovering: boolean, part: "name" | "role") => { + if (part === "role" && contributor.role === "original-dev") { if (isHovering) { timeoutID = setTimeout(() => { setAltIconName("classic"); @@ -177,7 +176,7 @@ function FooterLink(props: {children: ComponentChildren, text: string, url: stri } -type HoverCallback = (contributor: Contributor, isHovering: boolean) => void; +type HoverCallback = (contributor: Contributor, isHovering: boolean, part: "name" | "role") => void; function Contributors({data, onHover}: {data: ContributorList, onHover?: HoverCallback}) { return data.contributors.slice(0, 10).map((c, index, array) => { @@ -192,10 +191,10 @@ function Contributors({data, onHover}: {data: ContributorList, onHover?: HoverCa function ContributorListItem({data, onHover}: {data: Contributor, onHover?: HoverCallback}) { - const linkRef = useRef(null); + const roleRef = useRef(null); const roleString = (data.role) ? t(`about.contributor_roles.${data.role}`) : ""; - useTooltip(linkRef, (data.role) ? { + useTooltip(roleRef, (data.role) ? { title: t(`about.role_brief_history.${data.role}`), customClass: "about-dialog-brief-history-tooltip", placement: "bottom", @@ -205,17 +204,22 @@ function ContributorListItem({data, onHover}: {data: Contributor, onHover?: Hove return <> onHover?.(data, true)} - onMouseLeave={(e) => onHover?.(data, false)}> - + onMouseEnter={(e) => onHover?.(data, true, "name")} + onMouseLeave={(e) => onHover?.(data, false, "name")}> + {data.fullName ?? data.name} - {roleString &&  ({roleString})} + {roleString && onHover?.(data, true, "role")} + onMouseLeave={(e) => onHover?.(data, false, "role")}> + + ({roleString}) + } } From d61e399c67dbde0a87422e9990579e35ad0856e7 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Tue, 31 Mar 2026 21:48:14 +0300 Subject: [PATCH 49/83] style/about dialog: add an animation to the donate button --- apps/client/src/widgets/dialogs/about.css | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 9b707cb365..d3dcf55d61 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -111,6 +111,11 @@ &.donate-link { color: var(--donate-button-color); + + &:hover i { + animation: heartbeat 600ms ease-in-out; + animation-iteration-count: 3; + } } } } @@ -140,6 +145,18 @@ } } +@keyframes heartbeat { + 0% { + transform: scale(1); + } 50% { + transform: scale(1.15); + } 75% { + transform: scale(1); + } 100% { + transform: scale(1); + } +} + /* TODO: move to global styles */ .property-sheet-table { border-spacing: 0 2px; From d3c927ed88a19d2c35224b791bda47c8e5e0b29c Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 12 Apr 2026 20:59:29 +0300 Subject: [PATCH 50/83] style/about dialog: exclude the brackets from the contributor role tooltip cue --- apps/client/src/widgets/dialogs/about.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index cf0aad263f..37ddbc6ac8 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -213,12 +213,11 @@ function ContributorListItem({data, onHover}: {data: Contributor, onHover?: Hove {roleString && onHover?.(data, true, "role")} onMouseLeave={(e) => onHover?.(data, false, "role")}> - ({roleString}) + ({roleString}) } } From e6db4a51d14c83f585ffc1d608057912a20cec43 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 12 Apr 2026 21:13:00 +0300 Subject: [PATCH 51/83] client/about dialog: update the version format --- apps/client/src/translations/en/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 43b99652e4..17a283b09d 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1,8 +1,8 @@ { "about": { "version_label": "Version:", - "version": "app: {{appVersion}}, database: {{dbVersion}}, sync protocol: {{syncVersion}}", - "build_info": "{{buildDate}}, revision: ", + "version": "{{appVersion}} (database: {{dbVersion}}, sync protocol: {{syncVersion}})", + "build_info": "Build: {{buildDate}}, revision: ", "contributors_label": "Contributors:", "contributor_roles": { "lead-dev": "lead developer", From 7f75ab063811983dc94b0203906a336d70db78f1 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Thu, 16 Apr 2026 20:47:30 +0300 Subject: [PATCH 52/83] client/about dialog: define the strings for a brief history --- apps/client/src/translations/en/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 17a283b09d..17e7d113ac 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -9,8 +9,8 @@ "original-dev": "original developer" }, "role_brief_history": { - "lead-dev": "TODO", - "original-dev": "TODO" + "lead-dev": "Elian Doran founded TriliumNext in 2024, a community fork created after Zadam stepped back from the project. Zadam later transferred the original repository to the TriliumNext team, merging both projects back into one.", + "original-dev": "On 25th December 2017, Zadam released the first beta of Trilium (written with a single \"i\", unlike the flower). Dissatisfied with existing note organizers, he built a powerful self-hosted hierarchical knowledge base that gathered over 22,000 GitHub stars. In 2024, as life got busier, he placed the project into maintenance mode." }, "contributor_full_list": "See the entire community", "data_directory": "Data directory:", From 262c89d252e872b250fd3e881ec38850b9dd1093 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Thu, 16 Apr 2026 20:48:53 +0300 Subject: [PATCH 53/83] style/about dialog: tweak the tooltip clues --- apps/client/src/widgets/dialogs/about.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index d3dcf55d61..c3d67c09d7 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -71,6 +71,8 @@ .contributor-role { text-decoration: underline dotted var(--main-text-color); + text-underline-offset: 3px; + text-decoration-color: var(--muted-text-color); cursor: help; } } @@ -122,7 +124,11 @@ } .about-dialog-brief-history-tooltip { - --main-font-size: .85em; + --main-font-size: .9em; + + .tooltip-inner { + max-width: 600px; + } } @keyframes icon-intro { From 12a83510ed442ead6beb4ad6941f95350f166cfa Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Thu, 16 Apr 2026 20:59:05 +0300 Subject: [PATCH 54/83] style/about dialog: add a different background color for nightly builds --- apps/client/src/widgets/dialogs/about.css | 8 ++++++++ apps/client/src/widgets/dialogs/about.tsx | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index c3d67c09d7..023a62d8e7 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -2,10 +2,18 @@ body.light-theme & { --donate-button-color: #e33f3b; + + &.nightly { + --modal-background-color: #f2e1ff; + } } body.dark-theme & { --donate-button-color: #fba6a5; + + &.nightly { + --modal-background-color: #23182b; + } } --bs-modal-width: 680px; diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 37ddbc6ac8..5dbeb7d3b2 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -15,6 +15,7 @@ import { Fragment } from "preact/jsx-runtime"; import { ComponentChildren } from "preact"; import { useMemo } from "react"; import { memo } from "preact/compat"; +import clsx from "clsx"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); @@ -65,7 +66,7 @@ export default function AboutDialog() { return ( setShown(false)} From d65d7dba3fb6f2f0494f445e5cc3d856ef39972d Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Thu, 16 Apr 2026 21:04:06 +0300 Subject: [PATCH 55/83] client/about dialog: refactor --- apps/client/src/widgets/dialogs/about.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 5dbeb7d3b2..4cc3963864 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -19,21 +19,21 @@ import clsx from "clsx"; export default function AboutDialog() { const [appInfo, setAppInfo] = useState(null); - const [shown, setShown] = useState(false); + const [isShown, setIsShown] = useState(false); const [isNightly, setNightly] = useState(false); - const [iconName, setIconName] = useState("default"); - const [altIconName, setAltIconName] = useState(null); + const [icon, setIcon] = useState("default"); + const [altIcon, setAltIcon] = useState(null); const onLoad = useCallback(async () => { if (!appInfo) { const info = await server.get("app-info"); if (info.appVersion.includes("test")) { setNightly(true); - setIconName("nightly"); + setIcon("nightly"); } setAppInfo(info); } - setShown(true); + setIsShown(true); }, []); useTriliumEvent("openAboutDialog", onLoad); @@ -44,11 +44,11 @@ export default function AboutDialog() { if (part === "role" && contributor.role === "original-dev") { if (isHovering) { timeoutID = setTimeout(() => { - setAltIconName("classic"); + setAltIcon("classic"); }, 500); } else { clearTimeout(timeoutID); - setAltIconName(null); + setAltIcon(null); } } } @@ -68,12 +68,12 @@ export default function AboutDialog() { setShown(false)} + show={isShown} + onHidden={() => setIsShown(false)} >
-
+

Trilium Notes {isNightly && Nightly}

triliumnotes.org From 4bc1d93b75cc414efcff200ffd8701551293288f Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Thu, 16 Apr 2026 21:07:47 +0300 Subject: [PATCH 56/83] style/about dialog: lower CSS selector specificity --- apps/client/src/widgets/dialogs/about.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 023a62d8e7..06e4d9f47c 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -1,6 +1,6 @@ .about-dialog { - body.light-theme & { + :where(body.light-theme &) { --donate-button-color: #e33f3b; &.nightly { @@ -8,9 +8,9 @@ } } - body.dark-theme & { + :where(body.dark-theme &) { --donate-button-color: #fba6a5; - + &.nightly { --modal-background-color: #23182b; } From d240fb32bb691f73d91d26f05f7f455bd6a9df08 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 09:49:05 +0300 Subject: [PATCH 57/83] style/about dialog: refactor --- apps/client/src/widgets/dialogs/about.css | 21 +++++++------ apps/client/src/widgets/dialogs/about.tsx | 38 +++++++++++------------ 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 06e4d9f47c..54f0a8a4b9 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -173,10 +173,12 @@ /* TODO: move to global styles */ .property-sheet-table { + display: table; border-spacing: 0 2px; border-collapse: separate; - tr { + dl { + display: table-row; --_br: 8px; background: var(--card-background-color); @@ -190,17 +192,18 @@ } } - td { + dt, dd { + display: table-cell; padding: 10px 16px; vertical-align: top; + } - &:first-child { - white-space: nowrap; - color: var(--muted-text-color); - } + dt { + white-space: nowrap; + color: var(--muted-text-color); + } - &:last-child { - width: 100%; - } + dl { + width: 100%; } } \ No newline at end of file diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 4cc3963864..e15dd31902 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -79,10 +79,10 @@ export default function AboutDialog() { triliumnotes.org - - - - - - - - - - - - - - - -
{t("about.version_label")} +
+
+
{t("about.version_label")}
+
{t("about.version", { appVersion: appInfo?.appVersion, dbVersion: appInfo?.dbVersion, @@ -99,27 +99,27 @@ export default function AboutDialog() { }} />
-
{t("about.contributors_label")} + + + +
+
{t("about.contributors_label")}
+
{t("about.contributor_full_list")} -
{t("about.data_directory")} + + + +
+
{t("about.data_directory")}
+
{appInfo?.dataDirectory && ()} -
+ + +
From 5539c901feead2deef1ae865d2d29fcb965f9b1a Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 10:20:30 +0300 Subject: [PATCH 58/83] style/about dialog: make the property sheet responsive --- apps/client/src/widgets/dialogs/about.css | 88 +++++++++++++++++------ 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 54f0a8a4b9..8ccec8cc35 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -173,37 +173,81 @@ /* TODO: move to global styles */ .property-sheet-table { - display: table; - border-spacing: 0 2px; - border-collapse: separate; + --border-radius: 8px; dl { - display: table-row; - --_br: 8px; - background: var(--card-background-color); - &:first-child { - clip-path: inset(0 round var(--_br) var(--_br) 0 0); - } - - &:last-child { - clip-path: inset(0 round 0 0 var(--_br) var(--_br)); + dt { + color: var(--muted-text-color); } } +} - dt, dd { - display: table-cell; - padding: 10px 16px; - vertical-align: top; +@media (min-width: 600px) { + .property-sheet-table { + display: table; + border-spacing: 0 2px; + border-collapse: separate; + + dl { + display: table-row; + --_br: var(--border-radius); + + &:first-child { + clip-path: inset(0 round var(--_br) var(--_br) 0 0); + } + + &:last-child { + clip-path: inset(0 round 0 0 var(--_br) var(--_br)); + } + } + + dt, dd { + display: table-cell; + padding: 10px 16px; + vertical-align: top; + } + + dt { + white-space: nowrap; + } + + dl { + width: 100%; } - - dt { - white-space: nowrap; - color: var(--muted-text-color); } +} - dl { - width: 100%; +@media (max-width: 599px) { + .property-sheet-table { + display: flex; + flex-direction: column; + gap: 2px; + + dl { + margin: 0; + padding: 12px 20px; + + &:first-child { + border-radius: var(--border-radius) var(--border-radius) 0 0; + } + + &:last-child { + border-radius: 0 0 var(--border-radius) var(--border-radius); + } + + dt { + margin-bottom: 8px; + font-size: .85em; + font-weight: 500; + text-transform: uppercase; + letter-spacing: .4pt; + } + + dd { + margin: 0; + } + } } } \ No newline at end of file From 426d5daf7339b65080d53aad27fe15b94f2aec2c Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 10:34:47 +0300 Subject: [PATCH 59/83] client/refactor: create a separate property sheet component --- .../src/widgets/react/PropertySheet.css | 79 +++++++++++++++++++ .../src/widgets/react/PropertySheet.tsx | 15 ++++ 2 files changed, 94 insertions(+) create mode 100644 apps/client/src/widgets/react/PropertySheet.css create mode 100644 apps/client/src/widgets/react/PropertySheet.tsx diff --git a/apps/client/src/widgets/react/PropertySheet.css b/apps/client/src/widgets/react/PropertySheet.css new file mode 100644 index 0000000000..65289ad697 --- /dev/null +++ b/apps/client/src/widgets/react/PropertySheet.css @@ -0,0 +1,79 @@ +.property-sheet-table { + --border-radius: 8px; + + dl { + background: var(--card-background-color); + + dt { + color: var(--muted-text-color); + } + } +} + +@media (min-width: 600px) { + .property-sheet-table { + display: table; + border-spacing: 0 2px; + border-collapse: separate; + + dl { + display: table-row; + --_br: var(--border-radius); + + &:first-child { + clip-path: inset(0 round var(--_br) var(--_br) 0 0); + } + + &:last-child { + clip-path: inset(0 round 0 0 var(--_br) var(--_br)); + } + } + + dt, dd { + display: table-cell; + padding: 10px 16px; + vertical-align: top; + } + + dt { + white-space: nowrap; + } + + dl { + width: 100%; + } + } +} + +@media (max-width: 599px) { + .property-sheet-table { + display: flex; + flex-direction: column; + gap: 2px; + + dl { + margin: 0; + padding: 12px 20px; + + &:first-child { + border-radius: var(--border-radius) var(--border-radius) 0 0; + } + + &:last-child { + border-radius: 0 0 var(--border-radius) var(--border-radius); + } + + dt { + margin-bottom: 8px; + font-size: .85em; + font-weight: 500; + text-transform: uppercase; + letter-spacing: .4pt; + } + + dd { + margin: 0; + } + } + } +} \ No newline at end of file diff --git a/apps/client/src/widgets/react/PropertySheet.tsx b/apps/client/src/widgets/react/PropertySheet.tsx new file mode 100644 index 0000000000..d359bd41bb --- /dev/null +++ b/apps/client/src/widgets/react/PropertySheet.tsx @@ -0,0 +1,15 @@ +import { ComponentChildren } from "preact"; +import "./PropertySheet.css"; + +export function PropertySheet({ children }: { children: ComponentChildren }) { + return
+ {children} +
+} + +export function PropertySheetItem({label, children}: {label: string, children: ComponentChildren}) { + return
+
{label}
+
{children}
+
+} \ No newline at end of file From 8cc5e0282e98feb1f951b08fe642ea502d5b4850 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 10:35:09 +0300 Subject: [PATCH 60/83] client/about dialog: refactor --- apps/client/src/widgets/dialogs/about.css | 81 ----------------------- apps/client/src/widgets/dialogs/about.tsx | 32 +++++---- 2 files changed, 15 insertions(+), 98 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 8ccec8cc35..7bd85e83b8 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -169,85 +169,4 @@ } 100% { transform: scale(1); } -} - -/* TODO: move to global styles */ -.property-sheet-table { - --border-radius: 8px; - - dl { - background: var(--card-background-color); - - dt { - color: var(--muted-text-color); - } - } -} - -@media (min-width: 600px) { - .property-sheet-table { - display: table; - border-spacing: 0 2px; - border-collapse: separate; - - dl { - display: table-row; - --_br: var(--border-radius); - - &:first-child { - clip-path: inset(0 round var(--_br) var(--_br) 0 0); - } - - &:last-child { - clip-path: inset(0 round 0 0 var(--_br) var(--_br)); - } - } - - dt, dd { - display: table-cell; - padding: 10px 16px; - vertical-align: top; - } - - dt { - white-space: nowrap; - } - - dl { - width: 100%; - } - } -} - -@media (max-width: 599px) { - .property-sheet-table { - display: flex; - flex-direction: column; - gap: 2px; - - dl { - margin: 0; - padding: 12px 20px; - - &:first-child { - border-radius: var(--border-radius) var(--border-radius) 0 0; - } - - &:last-child { - border-radius: 0 0 var(--border-radius) var(--border-radius); - } - - dt { - margin-bottom: 8px; - font-size: .85em; - font-weight: 500; - text-transform: uppercase; - letter-spacing: .4pt; - } - - dd { - margin: 0; - } - } - } } \ No newline at end of file diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index e15dd31902..4f49153955 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -7,6 +7,7 @@ import openService from "../../services/open.js"; import { useState, useCallback, useRef } from "preact/hooks"; import type { AppInfo, Contributor, ContributorList } from "@triliumnext/commons"; import { useTooltip, useTriliumEvent } from "../react/hooks.jsx"; +import { PropertySheet, PropertySheetItem } from "../react/PropertySheet.js"; import "./about.css"; import { Trans } from "react-i18next"; import type React from "react"; @@ -79,10 +80,9 @@ export default function AboutDialog() { triliumnotes.org -
-
-
{t("about.version_label")}
-
+ + +
{t("about.version", { appVersion: appInfo?.appVersion, dbVersion: appInfo?.dbVersion, @@ -99,27 +99,25 @@ export default function AboutDialog() { }} />
-
-
+
+ -
-
{t("about.contributors_label")}
-
+ +
-
+
+ -
-
{t("about.data_directory")}
-
+ +
{appInfo?.dataDirectory && ()} -
-
-
+
+ +
From 3ce2af9abe8edf70dad5a58388e2a625e657e4d6 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 10:44:59 +0300 Subject: [PATCH 61/83] client/property sheet component: add support for CSS class names --- apps/client/src/widgets/react/PropertySheet.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/client/src/widgets/react/PropertySheet.tsx b/apps/client/src/widgets/react/PropertySheet.tsx index d359bd41bb..0053ea4ddd 100644 --- a/apps/client/src/widgets/react/PropertySheet.tsx +++ b/apps/client/src/widgets/react/PropertySheet.tsx @@ -1,15 +1,16 @@ import { ComponentChildren } from "preact"; +import clsx from "clsx"; import "./PropertySheet.css"; -export function PropertySheet({ children }: { children: ComponentChildren }) { - return
+export function PropertySheet({ className, children }: { className?: string, children: ComponentChildren }) { + return
{children}
} -export function PropertySheetItem({label, children}: {label: string, children: ComponentChildren}) { +export function PropertySheetItem({className, label, children}: {className?: string, label: string, children: ComponentChildren}) { return
{label}
-
{children}
+
{children}
} \ No newline at end of file From 683814c9d24ed5de2be898f38494bb192106f8f4 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 10:47:20 +0300 Subject: [PATCH 62/83] client/about dialog: refactor --- apps/client/src/widgets/dialogs/about.tsx | 46 +++++++++---------- .../src/widgets/react/PropertySheet.css | 4 ++ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 4f49153955..9abcb30950 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -82,38 +82,34 @@ export default function AboutDialog() { -
- {t("about.version", { - appVersion: appInfo?.appVersion, - dbVersion: appInfo?.dbVersion, - syncVersion: appInfo?.syncVersion - })} -
- -
+ {t("about.version", { + appVersion: appInfo?.appVersion, + dbVersion: appInfo?.dbVersion, + syncVersion: appInfo?.syncVersion + })} +
+
- - + + {t("about.contributor_full_list")} + -
+
{appInfo?.dataDirectory && ()}
diff --git a/apps/client/src/widgets/react/PropertySheet.css b/apps/client/src/widgets/react/PropertySheet.css index 65289ad697..f41265511f 100644 --- a/apps/client/src/widgets/react/PropertySheet.css +++ b/apps/client/src/widgets/react/PropertySheet.css @@ -7,6 +7,10 @@ dt { color: var(--muted-text-color); } + + dd { + user-select: text; + } } } From 2c6c7cb03784ed4145d42f26f00cc3b48c1aefd9 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 11:15:18 +0300 Subject: [PATCH 63/83] client: create a fluid wrapper component --- .../client/src/widgets/react/FluidWrapper.tsx | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 apps/client/src/widgets/react/FluidWrapper.tsx diff --git a/apps/client/src/widgets/react/FluidWrapper.tsx b/apps/client/src/widgets/react/FluidWrapper.tsx new file mode 100644 index 0000000000..f11389c471 --- /dev/null +++ b/apps/client/src/widgets/react/FluidWrapper.tsx @@ -0,0 +1,41 @@ +import clsx from "clsx"; +import { ComponentChildren } from "preact"; +import { useEffect, useMemo, useRef, useState } from "preact/hooks"; + +interface FluidWrapperParams { + className?: string; + breakpoints: {[key: string]: number}; + children: ComponentChildren; +} + +export function FluidWrapper({className, breakpoints, children}: FluidWrapperParams) { + const ref = useRef(null); + const sortedBreakpoints = useMemo(() => { + return Object.entries(breakpoints).sort(([, a], [, b]) => a - b) + }, [breakpoints]); + const [activeBreakpoint, setActiveBreakpoint] = useState(null); + + useEffect(() => { + const el = ref.current; + if (!el) return; + + const onWidthChanged = (width: number) => { + let match = sortedBreakpoints[0]?.[0] ?? null; + for (const [name, min] of sortedBreakpoints) { + if (width >= min) match = name; + else break; + } + setActiveBreakpoint(match); + }; + + const observer = new ResizeObserver(([entry]) => onWidthChanged(entry.contentRect.width)); + observer.observe(el); + onWidthChanged(el.getBoundingClientRect().width); + + return () => observer.disconnect(); + }, [sortedBreakpoints]); + + return
+ {children} +
+} \ No newline at end of file From 7c3bb8c5895b6b4e28d80018ed6a33a19f47e996 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 11:22:16 +0300 Subject: [PATCH 64/83] client: refactor --- apps/client/src/widgets/dialogs/about.css | 3 +- apps/client/src/widgets/dialogs/about.tsx | 2 +- .../src/widgets/react/PropertySheet.css | 63 +++++++++---------- .../src/widgets/react/PropertySheet.tsx | 20 ++++-- 4 files changed, 49 insertions(+), 39 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 7bd85e83b8..2aa0ecc7b4 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -1,5 +1,4 @@ .about-dialog { - :where(body.light-theme &) { --donate-button-color: #e33f3b; @@ -61,7 +60,7 @@ align-items: center; } - .property-sheet-table { + .about-dialog-property-sheet { margin-block: 30px; font-size: .85em; margin-inline: 20px; diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 9abcb30950..0fa648aea1 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -80,7 +80,7 @@ export default function AboutDialog() { triliumnotes.org - + {t("about.version", { appVersion: appInfo?.appVersion, diff --git a/apps/client/src/widgets/react/PropertySheet.css b/apps/client/src/widgets/react/PropertySheet.css index f41265511f..e829526e77 100644 --- a/apps/client/src/widgets/react/PropertySheet.css +++ b/apps/client/src/widgets/react/PropertySheet.css @@ -1,6 +1,8 @@ -.property-sheet-table { +:where(.property-sheet) { --border-radius: 8px; +} +.property-sheet { dl { background: var(--card-background-color); @@ -14,8 +16,7 @@ } } -@media (min-width: 600px) { - .property-sheet-table { +.property-sheet-container.wide .property-sheet { display: table; border-spacing: 0 2px; border-collapse: separate; @@ -41,43 +42,41 @@ dt { white-space: nowrap; + font-weight: normal; } dl { width: 100%; } - } } -@media (max-width: 599px) { - .property-sheet-table { - display: flex; - flex-direction: column; - gap: 2px; +.property-sheet-container.narrow .property-sheet { + display: flex; + flex-direction: column; + gap: 2px; - dl { + dl { + margin: 0; + padding: 12px 20px; + + &:first-child { + border-radius: var(--border-radius) var(--border-radius) 0 0; + } + + &:last-child { + border-radius: 0 0 var(--border-radius) var(--border-radius); + } + + dt { + margin-bottom: 8px; + font-size: .85em; + font-weight: 500; + text-transform: uppercase; + letter-spacing: .4pt; + } + + dd { margin: 0; - padding: 12px 20px; - - &:first-child { - border-radius: var(--border-radius) var(--border-radius) 0 0; - } - - &:last-child { - border-radius: 0 0 var(--border-radius) var(--border-radius); - } - - dt { - margin-bottom: 8px; - font-size: .85em; - font-weight: 500; - text-transform: uppercase; - letter-spacing: .4pt; - } - - dd { - margin: 0; - } } } -} \ No newline at end of file +} diff --git a/apps/client/src/widgets/react/PropertySheet.tsx b/apps/client/src/widgets/react/PropertySheet.tsx index 0053ea4ddd..6f520580ea 100644 --- a/apps/client/src/widgets/react/PropertySheet.tsx +++ b/apps/client/src/widgets/react/PropertySheet.tsx @@ -1,11 +1,23 @@ import { ComponentChildren } from "preact"; import clsx from "clsx"; import "./PropertySheet.css"; +import { FluidWrapper } from "./FluidWrapper"; -export function PropertySheet({ className, children }: { className?: string, children: ComponentChildren }) { - return
- {children} -
+interface PropertySheetParams { + className?: string; + children: ComponentChildren; + wideLayoutBreakpoint?: number; +} + +export function PropertySheet({ className, children, wideLayoutBreakpoint }: PropertySheetParams) { + return + +
+ {children} +
+
} export function PropertySheetItem({className, label, children}: {className?: string, label: string, children: ComponentChildren}) { From e624c204ff166b804d17a3e5180b82e9260abb33 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 15:53:08 +0300 Subject: [PATCH 65/83] client/fluid container: fix a bug --- apps/client/src/widgets/react/FluidWrapper.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/react/FluidWrapper.tsx b/apps/client/src/widgets/react/FluidWrapper.tsx index f11389c471..09f47ff55e 100644 --- a/apps/client/src/widgets/react/FluidWrapper.tsx +++ b/apps/client/src/widgets/react/FluidWrapper.tsx @@ -35,7 +35,9 @@ export function FluidWrapper({className, breakpoints, children}: FluidWrapperPar return () => observer.disconnect(); }, [sortedBreakpoints]); - return
- {children} + return
+
+ {children} +
} \ No newline at end of file From e2c79ff047441aa027903946bbb8c989c3738bf5 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 15:54:10 +0300 Subject: [PATCH 66/83] client: refactor --- apps/client/src/widgets/react/PropertySheet.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/client/src/widgets/react/PropertySheet.tsx b/apps/client/src/widgets/react/PropertySheet.tsx index 6f520580ea..b196a956b3 100644 --- a/apps/client/src/widgets/react/PropertySheet.tsx +++ b/apps/client/src/widgets/react/PropertySheet.tsx @@ -9,12 +9,12 @@ interface PropertySheetParams { wideLayoutBreakpoint?: number; } -export function PropertySheet({ className, children, wideLayoutBreakpoint }: PropertySheetParams) { +export function PropertySheet({className, children, wideLayoutBreakpoint}: PropertySheetParams) { return -
+
{children}
From 83d08d890d19e1cb1a87afde4d2385d6ce349074 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 16:05:09 +0300 Subject: [PATCH 67/83] style/about dialog: improve appearance --- apps/client/src/widgets/dialogs/about.css | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.css b/apps/client/src/widgets/dialogs/about.css index 2aa0ecc7b4..c3ad80b235 100644 --- a/apps/client/src/widgets/dialogs/about.css +++ b/apps/client/src/widgets/dialogs/about.css @@ -62,8 +62,11 @@ .about-dialog-property-sheet { margin-block: 30px; - font-size: .85em; - margin-inline: 20px; + + &.wide { + font-size: .85em; + margin-inline: 20px; + } } .build-info { @@ -82,6 +85,10 @@ text-decoration-color: var(--muted-text-color); cursor: help; } + + .about-dialog-property-sheet.narrow & { + line-height: 1.75; + } } footer { @@ -132,6 +139,7 @@ .about-dialog-brief-history-tooltip { --main-font-size: .9em; + padding-inline: 30px; .tooltip-inner { max-width: 600px; From c031a99fc042330a7a6ccf5b87820d203563fae0 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 16:07:39 +0300 Subject: [PATCH 68/83] client/about dialog: update the license URL --- apps/client/src/widgets/dialogs/about.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 0fa648aea1..2e5de6907f 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -127,7 +127,7 @@ export default function AboutDialog() { {/* https://pictogrammers.com/library/mdi/icon/scale-balance/ */} From 2742df7cd1fdff701849bd3595c39837783c93c9 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 16:10:00 +0300 Subject: [PATCH 69/83] client/about dialog: fix a typo --- apps/client/src/translations/en/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index bde840adb0..9271e290a7 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -10,7 +10,7 @@ }, "role_brief_history": { "lead-dev": "Elian Doran founded TriliumNext in 2024, a community fork created after Zadam stepped back from the project. Zadam later transferred the original repository to the TriliumNext team, merging both projects back into one.", - "original-dev": "On 25th December 2017, Zadam released the first beta of Trilium (written with a single \"i\", unlike the flower). Dissatisfied with existing note organizers, he built a powerful self-hosted hierarchical knowledge base that gathered over 22,000 GitHub stars. In 2024, as life got busier, he placed the project into maintenance mode." + "original-dev": "On 25th December 2017, Zadam released the first beta of Trilium (written with a single \"l\", unlike the flower). Dissatisfied with existing note organizers, he built a powerful self-hosted hierarchical knowledge base that gathered over 22,000 GitHub stars. In 2024, as life got busier, he placed the project into maintenance mode." }, "contributor_full_list": "See the entire community", "data_directory": "Data directory:", From dbea88fa56a9e7905844a23dafb664af5abf570f Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 20:20:06 +0300 Subject: [PATCH 70/83] client/modal dialog: add support for full page dialogs on mobile --- apps/client/src/stylesheets/style.css | 15 +++++++++++++++ apps/client/src/widgets/react/Modal.tsx | 8 ++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css index c1672f50c4..f9bc58362e 100644 --- a/apps/client/src/stylesheets/style.css +++ b/apps/client/src/stylesheets/style.css @@ -1666,6 +1666,21 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu { display: flex; } + body.mobile .modal-dialog.modal-dialog-full-page-on-mobile { + width: 100%; + height: 100%; + max-height: unset; + + .modal-content { + border-radius: 0; + border: 0; + + .modal-body { + overflow: scroll; + } + } + } + body.mobile .modal-content { overflow-y: auto; border-radius: var(--bs-modal-border-radius) var(--bs-modal-border-radius) 0 0; diff --git a/apps/client/src/widgets/react/Modal.tsx b/apps/client/src/widgets/react/Modal.tsx index fcaa287380..0a3d35fa79 100644 --- a/apps/client/src/widgets/react/Modal.tsx +++ b/apps/client/src/widgets/react/Modal.tsx @@ -77,9 +77,13 @@ export interface ModalProps { * If true, the modal will not focus itself after becoming visible. */ noFocus?: boolean; + /** + * Indicates if the dialog will be displayed as a full page on mobile devices. + */ + isFullPageOnMobile?: boolean; } -export default function Modal({ children, className, size, title, customTitleBarButtons: titleBarButtons, header, footer, footerStyle, footerAlignment, onShown, onSubmit, helpPageId, minWidth, maxWidth, zIndex, scrollable, onHidden, modalRef: externalModalRef, formRef, bodyStyle, show, stackable, keepInDom, noFocus }: ModalProps) { +export default function Modal({ children, className, size, title, customTitleBarButtons: titleBarButtons, header, footer, footerStyle, footerAlignment, onShown, onSubmit, helpPageId, minWidth, maxWidth, zIndex, scrollable, onHidden, modalRef: externalModalRef, formRef, bodyStyle, show, stackable, keepInDom, noFocus, isFullPageOnMobile }: ModalProps) { const modalRef = useSyncedRef(externalModalRef); const modalInstanceRef = useRef(); const elementToFocus = useRef(); @@ -143,7 +147,7 @@ export default function Modal({ children, className, size, title, customTitleBar return (
- {(show || keepInDom) &&
+ {(show || keepInDom) &&
{!title || typeof title === "string" ? ( From b349922addab22506f489b90dd5fd0b3d11954e6 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 20:20:39 +0300 Subject: [PATCH 71/83] client/about dialog: make full page on mobile --- apps/client/src/widgets/dialogs/about.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 2e5de6907f..755c45a072 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -69,6 +69,7 @@ export default function AboutDialog() { setIsShown(false)} > From 36270397c23c51ad25c37b59d809c2b9b01b1655 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 20:21:52 +0300 Subject: [PATCH 72/83] git mailmap: update --- .mailmap | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index 33287fa2ca..2aaad92883 100644 --- a/.mailmap +++ b/.mailmap @@ -1,2 +1,115 @@ -zadam -zadam \ No newline at end of file +# Format: Canonical Name +# Merges aliases so `git shortlog`, `git log --use-mailmap`, etc. group commits per person. + +# Core maintainers +zadam +zadam +zadam + +Elian Doran +Elian Doran + +Adorian Doran +Adorian Doran + +# Contributors with multiple emails / name variants +Panagiotis Papadopoulos <102623907+pano9000@users.noreply.github.com> + +Jon Fuller + +SiriusXT <11609255001@qq.com> +SiriusXT <11609255001@qq.com> <37627919+SiriusXT@users.noreply.github.com> + +JYC333 <22962980+JYC333@users.noreply.github.com> +JYC333 <22962980+JYC333@users.noreply.github.com> + +Nriver <6752679+Nriver@users.noreply.github.com> + +Francis C. +Francis C. + +Thomas Frei <7283497+thfrei@users.noreply.github.com> + +hasecilu + +meinzzzz + +FliegendeWurst +FliegendeWurst <2012gdwu@web.de> +FliegendeWurst <2012gdwu+github@posteo.de> + +MeIchthys +MeIchthys <10717998+meichthys@users.noreply.github.com> + +Marcel Wiechmann +Marcel Wiechmann + +Tomas Adamek +Tomas Adamek <50672285+Kureii@users.noreply.github.com> + +soulsands <407221377@qq.com> + +chesspro13 + +sigaloid <69441971+sigaloid@users.noreply.github.com> + +Marek Lewandowski +Marek Lewandowski +Marek Lewandowski + +lzinga +lzinga + +Sukant Gujar + +Matt Wilkie +Matt Wilkie + +Andreas Haan + +Potjoe-97 <42873357+Potjoe-97@users.noreply.github.com> +Potjoe-97 <42873357+Potjoe-97@users.noreply.github.com> + +Alex Pietsch <54153428+alexpietsch@users.noreply.github.com> + +Laurent Cozic +Laurent Cozic + +Zexin Yuan +Zexin Yuan + +hulmgulm +hulmgulm <12165268+hulmgulm@users.noreply.github.com> +hulmgulm + +Jules Bertholet + +Charles Dagenais + +Giulia Ye + +baddate <37013819+baddate@users.noreply.github.com> + +DerVogel101 <128903814+DerVogel101@users.noreply.github.com> +DerVogel101 <128903814+DerVogel101@users.noreply.github.com> + +Marcello Fuschi + +Jiahao Lee + +Dmitry Matveyev +Dmitry Matveyev + +Grant Zhu + +Sylvain Pasche +Sylvain Pasche + +mm21 <8033134+mm21@users.noreply.github.com> +mm21 <8033134+mm21@users.noreply.github.com> + +BeatLink +BeatLink + +Florian Meißner <161936+Mystler@users.noreply.github.com> +Florian Meißner <161936+Mystler@users.noreply.github.com> From 1d8a8107af8ca5edc8b51ff2f4f8f3c0e7615ceb Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 20:29:49 +0300 Subject: [PATCH 73/83] style/property sheet component: improve appearance --- apps/client/src/widgets/react/PropertySheet.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/client/src/widgets/react/PropertySheet.css b/apps/client/src/widgets/react/PropertySheet.css index e829526e77..4416e47cb5 100644 --- a/apps/client/src/widgets/react/PropertySheet.css +++ b/apps/client/src/widgets/react/PropertySheet.css @@ -7,7 +7,7 @@ background: var(--card-background-color); dt { - color: var(--muted-text-color); + opacity: .75; } dd { @@ -57,7 +57,7 @@ dl { margin: 0; - padding: 12px 20px; + padding: 16px 20px; &:first-child { border-radius: var(--border-radius) var(--border-radius) 0 0; @@ -70,9 +70,9 @@ dt { margin-bottom: 8px; font-size: .85em; - font-weight: 500; + font-weight: 550; text-transform: uppercase; - letter-spacing: .4pt; + letter-spacing: .75pt; } dd { From ac1bcefe80f442ac88ff8b5a3a052b1d08a71156 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 18 Apr 2026 20:31:48 +0300 Subject: [PATCH 74/83] client/property sheet component: fix width limit on mobile --- apps/client/src/stylesheets/style.css | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css index f9bc58362e..5d35dbfb14 100644 --- a/apps/client/src/stylesheets/style.css +++ b/apps/client/src/stylesheets/style.css @@ -1670,6 +1670,7 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu { width: 100%; height: 100%; max-height: unset; + max-width: unset; .modal-content { border-radius: 0; From 900f308349fb8a26a819c879e4e302bab1b8ad98 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 19 Apr 2026 11:10:45 +0300 Subject: [PATCH 75/83] contributor list: switch to manual update, add a tool for a reference list (local Git + GitHub listing) --- contributors.json | 22 +++----- package.json | 2 +- scripts/list-contributors.ts | 87 ++++++++++++++++++++++++++++++ scripts/update-contributor-list.ts | 75 -------------------------- 4 files changed, 95 insertions(+), 91 deletions(-) create mode 100644 scripts/list-contributors.ts delete mode 100644 scripts/update-contributor-list.ts diff --git a/contributors.json b/contributors.json index 59057dfa33..86f480074c 100644 --- a/contributors.json +++ b/contributors.json @@ -1,5 +1,5 @@ { - "⚠️": "NOTE: this is an auto-generated list. Do not modify it.", + "ℹ️": "Use `npm run list-contributors`. as a reference to update this list.", "contributors": [ { "name": "eliandoran", @@ -17,37 +17,29 @@ "name": "adoriandoran", "url": "https://github.com/adoriandoran" }, - { - "name": "pano9000", - "url": "https://github.com/pano9000" - }, { "name": "perfectra1n", "url": "https://github.com/perfectra1n" }, { - "name": "JYC333", - "url": "https://github.com/JYC333" + "name": "pano9000", + "url": "https://github.com/pano9000" }, { "name": "SiriusXT", "url": "https://github.com/SiriusXT" }, { - "name": "tony", - "url": "https://github.com/tony" - }, - { - "name": "Nriver", - "url": "https://github.com/Nriver" + "name": "JYC333", + "url": "https://github.com/JYC333" }, { "name": "francistw", "url": "https://github.com/francistw" }, { - "name": "isaul32", - "url": "https://github.com/isaul32" + "name": "Nriver", + "url": "https://github.com/Nriver" }, { "name": "thfrei", diff --git a/package.json b/package.json index 91ef5a4c4d..17254a5179 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "dev:linter-fix": "cross-env NODE_OPTIONS=--max_old_space_size=4096 eslint . --fix", "postinstall": "tsx scripts/electron-rebuild.mts && pnpm prepare", "prepare": "pnpm run --filter pdfjs-viewer --filter share-theme build && pnpm run --filter web-clipper postinstall", - "update-contributor-list": "tsx ./scripts/update-contributor-list.ts" + "list-contributors": "tsx ./scripts/list-contributors.ts" }, "private": true, "devDependencies": { diff --git a/scripts/list-contributors.ts b/scripts/list-contributors.ts new file mode 100644 index 0000000000..02a7c534b7 --- /dev/null +++ b/scripts/list-contributors.ts @@ -0,0 +1,87 @@ +import { execSync} from "node:child_process"; + +interface ContributorInfo { + name: string; + fullName?: string + email?: string; + commitCount: number; + url: string; +} + +interface showTableParams { + title: string; + comment?: string; + contributors: ContributorInfo[]; + columns: (keyof ContributorInfo)[]; +} + +async function main() { + listLocalGitContributors(); + await listGitHubContributors(); +} + +async function listGitHubContributors() { + let list: any[] | null = null; + + const response = await fetch("https://api.github.com/repos/TriliumNext/Trilium/contributors"); + if (response.ok) { + list = await response.json(); + } else { + console.error(`Unable to request the contributor list from GitHub. Reason: ${response.statusText}`); + } + + if (!list) { + return; + } + + const contributors: ContributorInfo[] = list.map((c) => { + return { + name: c.login, + url: c.html_url, + commitCount: c.contributions + } as ContributorInfo; + }); + + showTable({ + title: "GitHub Contributor List", + comment: "Note: the GitHub list also include contributors that did not directly contribute to Trilium, but to submodules used in the Trilium's repo.", + contributors: contributors, + columns: ["name", "url", "commitCount"] + }); +} + +function listLocalGitContributors() { + const rawOutput = execSync("git shortlog -sne --no-merges HEAD -- src/ apps/") + .toString() + .split("\n") + .slice(0, 20); + + const contributors: ContributorInfo[] = rawOutput.map((line: string) => { + const match = line.match(/^\s*(\d+)\s+(.+?)\s+<(.+)>$/); + if (!match) { + return null; + } + return { + name: match[2], + email: match[3], + commitCount: parseInt(match[1]) + } + }); + + showTable({ + title: "Local Git Contributor List", + comment: "", + columns: ["name", "email", "commitCount"], + contributors: contributors.filter((c: ContributorInfo | null) => c !== null) + }); +} + +function showTable(params: showTableParams) { + console.log(`\n──── ${params.title} ────`); + if (params.comment) { + console.log(`\n${params.comment}\n`); + } + console.table(params.contributors, params.columns); +} + +main(); \ No newline at end of file diff --git a/scripts/update-contributor-list.ts b/scripts/update-contributor-list.ts deleted file mode 100644 index d6478c2da4..0000000000 --- a/scripts/update-contributor-list.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Contributor, ContributorList } from "../packages/commons/"; -import { writeFileSync } from "fs"; - -// Keep honorific contributors at top of the list, even if their commit count -// is exceeded by another users. -const PINNED_CONTRIBUTORS: Record> = { - "eliandoran": {fullName: "Elian Doran", role: "lead-dev"}, - "zadam": {fullName: "Zadam", role: "original-dev"} -}; - -// Bots marked as users on the GitHub profile info to exclude from the listing -const BOTS = [ - "weblate" -]; - -async function main() { - console.log("Retrieving the contributor list..."); - - let data: any = {}; - try { - data = await fetchContributors(); - } catch (ex) { - console.error(ex); - return; - } - - writeFileSync("contributors.json", JSON.stringify(data, null, 4)); - console.log("Done."); -} - -async function fetchContributors() { - const response = await fetch("https://api.github.com/repos/TriliumNext/Trilium/contributors"); - - if (response.ok) { - return { - "⚠️": "NOTE: this is an auto-generated list. Do not modify it.", - contributors: processContributorList(await response.json()) - } as ContributorList - } else { - throw new Error(`Unable to request the contributor list from GitHub. Reason: ${response.statusText}`); - } -} - -function processContributorList(contributorInfo: GithubContributor[]) { - return contributorInfo - // Filter out bots - .filter((c) => c.type === "User" && !BOTS.includes(c.login)) - // Sort by the commit count. Honorific contributors are always first. - .sort(contributorOrderer) - .map((c) => { - let pinnedInfo = PINNED_CONTRIBUTORS[c.login]; - - return { - name: c.login, - fullName: pinnedInfo?.fullName, - url: c.html_url, - role: pinnedInfo?.role - } as Contributor; - }); -} - -function contributorOrderer(a, b) { - const isAPinned = (a.login in PINNED_CONTRIBUTORS); - const isBPinned = (b.login in PINNED_CONTRIBUTORS); - - // Pinned contributors come first - if (isAPinned !== isBPinned) { - return isAPinned ? -1 : 1; - } - - // Within each group, sort by contributions - return b.contributions - a.contributions; -} - -main(); \ No newline at end of file From e6e6c1feffa4116871035b2ba76e412c13e5dc89 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 19 Apr 2026 13:03:57 +0300 Subject: [PATCH 76/83] chore(scripts): filter contributors by role --- scripts/list-contributors.ts | 60 +++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/scripts/list-contributors.ts b/scripts/list-contributors.ts index 02a7c534b7..8df2652e8b 100644 --- a/scripts/list-contributors.ts +++ b/scripts/list-contributors.ts @@ -5,7 +5,9 @@ interface ContributorInfo { fullName?: string email?: string; commitCount: number; - url: string; + translationCommitCount?: number; + role?: string; + url?: string; } interface showTableParams { @@ -50,29 +52,51 @@ async function listGitHubContributors() { }); } -function listLocalGitContributors() { - const rawOutput = execSync("git shortlog -sne --no-merges HEAD -- src/ apps/") - .toString() - .split("\n") - .slice(0, 20); - - const contributors: ContributorInfo[] = rawOutput.map((line: string) => { +const TRANSLATION_PATHS = [ + "apps/client/src/translations/", + "apps/server/src/assets/translations/" +]; + +function parseShortlog(rawOutput: string): Map { + const result = new Map(); + for (const line of rawOutput.split("\n")) { const match = line.match(/^\s*(\d+)\s+(.+?)\s+<(.+)>$/); - if (!match) { - return null; + if (match) { + result.set(match[2], { email: match[3], commitCount: parseInt(match[1]) }); } - return { - name: match[2], - email: match[3], - commitCount: parseInt(match[1]) - } - }); + } + return result; +} + +function listLocalGitContributors() { + const allOutput = execSync("git shortlog -sne --no-merges HEAD -- src/ apps/").toString(); + const translationOutput = execSync(`git shortlog -sne --no-merges HEAD -- ${TRANSLATION_PATHS.join(" ")}`).toString(); + + const allContribs = parseShortlog(allOutput); + const translationContribs = parseShortlog(translationOutput); + + const contributors: ContributorInfo[] = []; + let rank = 0; + for (const [name, { email, commitCount }] of allContribs) { + if (++rank > 20) break; + + const translationCommitCount = translationContribs.get(name)?.commitCount ?? 0; + const role = translationCommitCount > commitCount * 0.5 ? "translator" : undefined; + + contributors.push({ + name, + email, + commitCount, + translationCommitCount, + role + }); + } showTable({ title: "Local Git Contributor List", comment: "", - columns: ["name", "email", "commitCount"], - contributors: contributors.filter((c: ContributorInfo | null) => c !== null) + columns: ["name", "email", "commitCount", "translationCommitCount", "role"], + contributors }); } From e7f85bb4475323afd9c0cfe5d00e49e7649000e7 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 19 Apr 2026 13:12:41 +0300 Subject: [PATCH 77/83] chore(scripts): filter contributors by minimum commits --- scripts/list-contributors.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/scripts/list-contributors.ts b/scripts/list-contributors.ts index 8df2652e8b..c70561bcae 100644 --- a/scripts/list-contributors.ts +++ b/scripts/list-contributors.ts @@ -36,13 +36,16 @@ async function listGitHubContributors() { return; } - const contributors: ContributorInfo[] = list.map((c) => { - return { - name: c.login, - url: c.html_url, - commitCount: c.contributions - } as ContributorInfo; - }); + const MIN_CONTRIBUTIONS = 125; + const contributors: ContributorInfo[] = list + .filter((c) => c.contributions >= MIN_CONTRIBUTIONS) + .map((c) => { + return { + name: c.login, + url: c.html_url, + commitCount: c.contributions + } as ContributorInfo; + }); showTable({ title: "GitHub Contributor List", From a403aca05467edf22e90589721085ed8ec55fd77 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 19 Apr 2026 13:16:08 +0300 Subject: [PATCH 78/83] chore(scripts): group translators separately --- scripts/list-contributors.ts | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/scripts/list-contributors.ts b/scripts/list-contributors.ts index c70561bcae..d465ca071e 100644 --- a/scripts/list-contributors.ts +++ b/scripts/list-contributors.ts @@ -78,28 +78,36 @@ function listLocalGitContributors() { const allContribs = parseShortlog(allOutput); const translationContribs = parseShortlog(translationOutput); - const contributors: ContributorInfo[] = []; + const developers: ContributorInfo[] = []; + const translators: ContributorInfo[] = []; let rank = 0; for (const [name, { email, commitCount }] of allContribs) { if (++rank > 20) break; const translationCommitCount = translationContribs.get(name)?.commitCount ?? 0; - const role = translationCommitCount > commitCount * 0.5 ? "translator" : undefined; + const isTranslator = translationCommitCount > commitCount * 0.5; - contributors.push({ - name, - email, - commitCount, - translationCommitCount, - role - }); + const entry: ContributorInfo = { name, email, commitCount }; + + if (isTranslator) { + translators.push(entry); + } else { + developers.push(entry); + } } showTable({ - title: "Local Git Contributor List", + title: "Local Git Contributors (Developers)", comment: "", - columns: ["name", "email", "commitCount", "translationCommitCount", "role"], - contributors + columns: ["name", "email", "commitCount"], + contributors: developers + }); + + showTable({ + title: "Local Git Contributors (Translators)", + comment: "Contributors where >50% of commits are to translation files.", + columns: ["name", "email", "commitCount"], + contributors: translators }); } From adc648d277b5fbe6df932b114c66f072a437a01b Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 19 Apr 2026 15:34:14 +0300 Subject: [PATCH 79/83] chore(scripts): tweak criteria for contributors --- .mailmap | 5 +++-- scripts/list-contributors.ts | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index 2aaad92883..1f9f14f5a8 100644 --- a/.mailmap +++ b/.mailmap @@ -17,8 +17,9 @@ Panagiotis Papadopoulos <102623907+pano9000@users.noreply.gith Jon Fuller -SiriusXT <11609255001@qq.com> -SiriusXT <11609255001@qq.com> <37627919+SiriusXT@users.noreply.github.com> +SiriusXT <1160925501@qq.com> +SiriusXT <1160925501@qq.com> <11609255001@qq.com> +SiriusXT <1160925501@qq.com> <37627919+SiriusXT@users.noreply.github.com> JYC333 <22962980+JYC333@users.noreply.github.com> JYC333 <22962980+JYC333@users.noreply.github.com> diff --git a/scripts/list-contributors.ts b/scripts/list-contributors.ts index d465ca071e..719898d679 100644 --- a/scripts/list-contributors.ts +++ b/scripts/list-contributors.ts @@ -60,6 +60,13 @@ const TRANSLATION_PATHS = [ "apps/server/src/assets/translations/" ]; +/** Authors that are bots or automated tools, not real contributors. */ +const EXCLUDED_AUTHORS = new Set([ + "Languages add-on", + "Hosted Weblate", + "renovate[bot]" +]); + function parseShortlog(rawOutput: string): Map { const result = new Map(); for (const line of rawOutput.split("\n")) { @@ -82,6 +89,7 @@ function listLocalGitContributors() { const translators: ContributorInfo[] = []; let rank = 0; for (const [name, { email, commitCount }] of allContribs) { + if (EXCLUDED_AUTHORS.has(name)) continue; if (++rank > 20) break; const translationCommitCount = translationContribs.get(name)?.commitCount ?? 0; From 2c744122cab1cfe036fb78cd19f23e41b8c0b9c9 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 19 Apr 2026 15:44:52 +0300 Subject: [PATCH 80/83] chore: update contributors list --- contributors.json | 79 +----------- scripts/list-contributors.ts | 235 ++++++++++++++++++++++++----------- 2 files changed, 169 insertions(+), 145 deletions(-) diff --git a/contributors.json b/contributors.json index 86f480074c..5134dff233 100644 --- a/contributors.json +++ b/contributors.json @@ -1,5 +1,5 @@ { - "ℹ️": "Use `npm run list-contributors`. as a reference to update this list.", + "ℹ️": "Auto-generated file. Run `pnpm run list-contributors` to regenerate.", "contributors": [ { "name": "eliandoran", @@ -15,14 +15,17 @@ }, { "name": "adoriandoran", + "fullName": "Adorian Doran", "url": "https://github.com/adoriandoran" }, { "name": "perfectra1n", + "fullName": "Jon Fuller", "url": "https://github.com/perfectra1n" }, { "name": "pano9000", + "fullName": "Panagiotis Papadopoulos", "url": "https://github.com/pano9000" }, { @@ -33,81 +36,9 @@ "name": "JYC333", "url": "https://github.com/JYC333" }, - { - "name": "francistw", - "url": "https://github.com/francistw" - }, { "name": "Nriver", "url": "https://github.com/Nriver" - }, - { - "name": "thfrei", - "url": "https://github.com/thfrei" - }, - { - "name": "nathancahill", - "url": "https://github.com/nathancahill" - }, - { - "name": "contributor", - "url": "https://github.com/contributor" - }, - { - "name": "FliegendeWurst", - "url": "https://github.com/FliegendeWurst" - }, - { - "name": "hasecilu", - "url": "https://github.com/hasecilu" - }, - { - "name": "Meinzzzz", - "url": "https://github.com/Meinzzzz" - }, - { - "name": "Sarah-Hussein", - "url": "https://github.com/Sarah-Hussein" - }, - { - "name": "zerebos", - "url": "https://github.com/zerebos" - }, - { - "name": "meichthys", - "url": "https://github.com/meichthys" - }, - { - "name": "questamor", - "url": "https://github.com/questamor" - }, - { - "name": "SukantGujar", - "url": "https://github.com/SukantGujar" - }, - { - "name": "soulsands", - "url": "https://github.com/soulsands" - }, - { - "name": "noobhjy", - "url": "https://github.com/noobhjy" - }, - { - "name": "laurent22", - "url": "https://github.com/laurent22" - }, - { - "name": "mlewand", - "url": "https://github.com/mlewand" - }, - { - "name": "lzinga", - "url": "https://github.com/lzinga" - }, - { - "name": "ytrkptl", - "url": "https://github.com/ytrkptl" } ] -} \ No newline at end of file +} diff --git a/scripts/list-contributors.ts b/scripts/list-contributors.ts index 719898d679..c58766d5f6 100644 --- a/scripts/list-contributors.ts +++ b/scripts/list-contributors.ts @@ -1,30 +1,152 @@ -import { execSync} from "node:child_process"; +import { execSync } from "node:child_process"; +import { readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; + +interface ContributorEntry { + name: string; + fullName?: string; + url: string; + role?: string; +} + +interface ContributorFile { + contributors: ContributorEntry[]; +} interface ContributorInfo { name: string; - fullName?: string + fullName?: string; email?: string; commitCount: number; - translationCommitCount?: number; - role?: string; url?: string; } -interface showTableParams { +interface ShowTableParams { title: string; comment?: string; contributors: ContributorInfo[]; columns: (keyof ContributorInfo)[]; } +const TRANSLATION_PATHS = [ + "apps/client/src/translations/", + "apps/server/src/assets/translations/" +]; + +/** Authors that are bots or automated tools, not real contributors. */ +const EXCLUDED_AUTHORS = new Set([ + "Languages add-on", + "Hosted Weblate", + "renovate[bot]" +]); + +const NOREPLY_PATTERN = /^(?:\d+\+)?(.+)@users\.noreply\.github\.com$/; + +/** + * Manual mapping for contributors whose git email doesn't reveal their + * GitHub username (i.e. no noreply email in .mailmap). + */ +const EMAIL_TO_GITHUB: Record = { + "contact@eliandoran.me": "eliandoran", + "zadam.apps@gmail.com": "zadam", + "adorian@esevo.ro": "adoriandoran", + "jonfuller2012@gmail.com": "perfectra1n", +}; + +const CONTRIBUTORS_PATH = join(__dirname, "..", "contributors.json"); + +/** + * Resolves a GitHub username from an email address. + * + * 1. Checks the manual mapping. + * 2. Extracts from GitHub noreply emails (e.g. "12345+user@…"). + * 3. Scans .mailmap for alternate emails that match the noreply pattern. + */ +function resolveGitHub(email: string, name: string): string | undefined { + if (EMAIL_TO_GITHUB[email]) return EMAIL_TO_GITHUB[email]; + + const noreply = email.match(NOREPLY_PATTERN); + if (noreply) return noreply[1]; + + // Grep .mailmap for alternate emails that match the noreply pattern + try { + const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const mailmapContent = execSync(`grep -i "${escapedName}" .mailmap 2>/dev/null`).toString(); + for (const line of mailmapContent.split("\n")) { + // Extract all emails from the line (inside angle brackets) + for (const [, email] of line.matchAll(/<([^>]+)>/g)) { + const match = email.match(NOREPLY_PATTERN); + if (match) return match[1]; + } + } + } catch { /* no matches */ } + + return undefined; +} + +function parseShortlog(rawOutput: string): Map { + const result = new Map(); + for (const line of rawOutput.split("\n")) { + const match = line.match(/^\s*(\d+)\s+(.+?)\s+<(.+)>$/); + if (match) { + result.set(match[2], { email: match[3], commitCount: parseInt(match[1]) }); + } + } + return result; +} + async function main() { - listLocalGitContributors(); + const { developers } = listLocalGitContributors(); await listGitHubContributors(); + updateContributorsJson(developers); +} + +function listLocalGitContributors() { + const allOutput = execSync("git shortlog -sne --no-merges HEAD -- src/ apps/").toString(); + const translationOutput = execSync(`git shortlog -sne --no-merges HEAD -- ${TRANSLATION_PATHS.join(" ")}`).toString(); + + const allContribs = parseShortlog(allOutput); + const translationContribs = parseShortlog(translationOutput); + + const developers: ContributorInfo[] = []; + const translators: ContributorInfo[] = []; + const MIN_COMMITS = 100; + for (const [name, { email, commitCount }] of allContribs) { + if (EXCLUDED_AUTHORS.has(name)) continue; + + const translationCommitCount = translationContribs.get(name)?.commitCount ?? 0; + const isTranslator = translationCommitCount > commitCount * 0.5; + + const githubUsername = resolveGitHub(email, name); + const url = githubUsername ? `https://github.com/${githubUsername}` : undefined; + const entry: ContributorInfo = { name, email, commitCount, url }; + + if (isTranslator) { + if (commitCount >= 20) translators.push(entry); + } else if (commitCount >= MIN_COMMITS) { + developers.push(entry); + } + } + + // showTable({ + // title: "Local Git Contributors (Developers)", + // columns: ["name", "url", "commitCount"], + // contributors: developers + // }); + + // showTable({ + // title: "Local Git Contributors (Translators)", + // comment: "Contributors where >50% of commits are to translation files.", + // columns: ["name", "url", "commitCount"], + // contributors: translators + // }); + + return { developers, translators }; } async function listGitHubContributors() { let list: any[] | null = null; - + const response = await fetch("https://api.github.com/repos/TriliumNext/Trilium/contributors"); if (response.ok) { list = await response.json(); @@ -47,79 +169,50 @@ async function listGitHubContributors() { } as ContributorInfo; }); - showTable({ - title: "GitHub Contributor List", - comment: "Note: the GitHub list also include contributors that did not directly contribute to Trilium, but to submodules used in the Trilium's repo.", - contributors: contributors, - columns: ["name", "url", "commitCount"] - }); + // showTable({ + // title: "GitHub Contributor List", + // comment: "Note: the GitHub list also include contributors that did not directly contribute to Trilium, but to submodules used in the Trilium's repo.", + // contributors: contributors, + // columns: ["name", "url", "commitCount"] + // }); } -const TRANSLATION_PATHS = [ - "apps/client/src/translations/", - "apps/server/src/assets/translations/" -]; +/** + * Updates contributors.json, preserving pinned entries (those with special + * roles like lead-dev, original-dev) and regenerating the rest from git data. + */ +function updateContributorsJson(developers: ContributorInfo[]) { + // Read existing file to preserve pinned entries + const existing: ContributorFile = JSON.parse(readFileSync(CONTRIBUTORS_PATH, "utf-8")); + const pinnedRoles = new Set(["lead-dev", "original-dev"]); + const pinned = existing.contributors.filter((c) => c.role && pinnedRoles.has(c.role)); -/** Authors that are bots or automated tools, not real contributors. */ -const EXCLUDED_AUTHORS = new Set([ - "Languages add-on", - "Hosted Weblate", - "renovate[bot]" -]); + // Build a set of pinned GitHub usernames to avoid duplicates + const pinnedNames = new Set(pinned.map((c) => c.name)); -function parseShortlog(rawOutput: string): Map { - const result = new Map(); - for (const line of rawOutput.split("\n")) { - const match = line.match(/^\s*(\d+)\s+(.+?)\s+<(.+)>$/); - if (match) { - result.set(match[2], { email: match[3], commitCount: parseInt(match[1]) }); - } - } - return result; -} + const contributors: ContributorEntry[] = [...pinned]; -function listLocalGitContributors() { - const allOutput = execSync("git shortlog -sne --no-merges HEAD -- src/ apps/").toString(); - const translationOutput = execSync(`git shortlog -sne --no-merges HEAD -- ${TRANSLATION_PATHS.join(" ")}`).toString(); + // Add developers (skip those already pinned) + for (const dev of developers) { + const githubName = dev.url?.replace("https://github.com/", ""); + if (!githubName || pinnedNames.has(githubName)) continue; - const allContribs = parseShortlog(allOutput); - const translationContribs = parseShortlog(translationOutput); - - const developers: ContributorInfo[] = []; - const translators: ContributorInfo[] = []; - let rank = 0; - for (const [name, { email, commitCount }] of allContribs) { - if (EXCLUDED_AUTHORS.has(name)) continue; - if (++rank > 20) break; - - const translationCommitCount = translationContribs.get(name)?.commitCount ?? 0; - const isTranslator = translationCommitCount > commitCount * 0.5; - - const entry: ContributorInfo = { name, email, commitCount }; - - if (isTranslator) { - translators.push(entry); - } else { - developers.push(entry); - } + contributors.push({ + name: githubName, + fullName: dev.name !== githubName ? dev.name : undefined, + url: dev.url! + }); } - showTable({ - title: "Local Git Contributors (Developers)", - comment: "", - columns: ["name", "email", "commitCount"], - contributors: developers - }); - - showTable({ - title: "Local Git Contributors (Translators)", - comment: "Contributors where >50% of commits are to translation files.", - columns: ["name", "email", "commitCount"], - contributors: translators - }); + const output = { + "ℹ️": "Auto-generated file. Run `pnpm run list-contributors` to regenerate.", + contributors + }; + writeFileSync(CONTRIBUTORS_PATH, JSON.stringify(output, null, 4) + "\n"); + console.log(`\n✅ Updated ${CONTRIBUTORS_PATH} with ${contributors.length} contributors.`); } -function showTable(params: showTableParams) { +function showTable(params: ShowTableParams) { console.log(`\n──── ${params.title} ────`); if (params.comment) { console.log(`\n${params.comment}\n`); @@ -127,4 +220,4 @@ function showTable(params: showTableParams) { console.table(params.contributors, params.columns); } -main(); \ No newline at end of file +main(); From b04945e7936e4d70bf2f34058d15d0bbd6a8fb3c Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 19 Apr 2026 15:47:13 +0300 Subject: [PATCH 81/83] chore(client): remove limit on contributors --- apps/client/src/widgets/dialogs/about.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 755c45a072..63caa07e43 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -175,7 +175,7 @@ function FooterLink(props: {children: ComponentChildren, text: string, url: stri type HoverCallback = (contributor: Contributor, isHovering: boolean, part: "name" | "role") => void; function Contributors({data, onHover}: {data: ContributorList, onHover?: HoverCallback}) { - return data.contributors.slice(0, 10).map((c, index, array) => { + return data.contributors.map((c, index, array) => { return From 5921d1f1a6f237b3f05a490a3035adba94206838 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 19 Apr 2026 18:21:33 +0300 Subject: [PATCH 82/83] client/about dialog: fix some issues --- apps/client/src/widgets/dialogs/about.tsx | 44 ++++++++++++----------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/apps/client/src/widgets/dialogs/about.tsx b/apps/client/src/widgets/dialogs/about.tsx index 63caa07e43..056426eaa9 100644 --- a/apps/client/src/widgets/dialogs/about.tsx +++ b/apps/client/src/widgets/dialogs/about.tsx @@ -13,9 +13,8 @@ import { Trans } from "react-i18next"; import type React from "react"; import contributors from "../../../../../contributors.json"; import { Fragment } from "preact/jsx-runtime"; -import { ComponentChildren } from "preact"; -import { useMemo } from "react"; -import { memo } from "preact/compat"; +import type { ComponentChildren } from "preact"; +import { useMemo, memo } from "preact/compat"; import clsx from "clsx"; export default function AboutDialog() { @@ -25,22 +24,26 @@ export default function AboutDialog() { const [icon, setIcon] = useState("default"); const [altIcon, setAltIcon] = useState(null); + const hasLoaded = useRef(false); + const onLoad = useCallback(async () => { - if (!appInfo) { + if (!hasLoaded.current) { const info = await server.get("app-info"); if (info.appVersion.includes("test")) { setNightly(true); setIcon("nightly"); } setAppInfo(info); + hasLoaded.current = true; + } setIsShown(true); }, []); useTriliumEvent("openAboutDialog", onLoad); - const createContributorHoverHandler = useCallback(() => { - let timeoutID; + const createContributorHoverHandler = () => { + let timeoutID: ReturnType; return (contributor: Contributor, isHovering: boolean, part: "name" | "role") => { if (part === "role" && contributor.role === "original-dev") { if (isHovering) { @@ -53,7 +56,7 @@ export default function AboutDialog() { } } } - }, []); + }; /* Cache the contributor list to prevent its rerendering. * When the icon changes, it triggers a rerender of the dialog. If this happens while an @@ -77,7 +80,7 @@ export default function AboutDialog() {

Trilium Notes {isNightly && Nightly}

- + triliumnotes.org @@ -95,7 +98,7 @@ export default function AboutDialog() { buildDate: appInfo?.buildDate ? formatDateTime(appInfo.buildDate) : "" }} components={{ - buildRevision: RevisionLink(appInfo) + buildRevision: as React.ReactElement }} />
@@ -104,7 +107,7 @@ export default function AboutDialog() { - + {t("about.contributor_full_list")} @@ -123,7 +126,7 @@ export default function AboutDialog() { url="https://github.com/TriliumNext/Trilium" tooltip={t("about.github_tooltip")}> - + - +
); } -function RevisionLink(appInfo: AppInfo | null) { +function RevisionLink({appInfo}: {appInfo: AppInfo | null}) { return <> - {appInfo?.buildRevision && + {appInfo?.buildRevision && {appInfo.buildRevision.substring(0, 7)} } - as React.ReactElement; + ; } function FooterLink(props: {children: ComponentChildren, text: string, url: string, tooltip: string, className?: string}) { @@ -166,7 +169,7 @@ function FooterLink(props: {children: ComponentChildren, text: string, url: stri placement: "bottom" }) - return + return {props.children} {props.text} @@ -202,16 +205,17 @@ function ContributorListItem({data, onHover}: {data: Contributor, onHover?: Hove onHover?.(data, true, "name")} - onMouseLeave={(e) => onHover?.(data, false, "name")}> + rel="noopener noreferrer" + onMouseEnter={() => onHover?.(data, true, "name")} + onMouseLeave={() => onHover?.(data, false, "name")}> {data.fullName ?? data.name} {roleString && onHover?.(data, true, "role")} - onMouseLeave={(e) => onHover?.(data, false, "role")}> + onMouseEnter={() => onHover?.(data, true, "role")} + onMouseLeave={() => onHover?.(data, false, "role")}> ({roleString}) } From c4d832eebc89c4e4c88e3ad8ce5c7c2d7947fc95 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 19 Apr 2026 18:33:35 +0300 Subject: [PATCH 83/83] contributor list: update script name, change a comment --- contributors.json | 11 +---------- package.json | 2 +- .../{list-contributors.ts => update-contributors.ts} | 2 +- 3 files changed, 3 insertions(+), 12 deletions(-) rename scripts/{list-contributors.ts => update-contributors.ts} (98%) diff --git a/contributors.json b/contributors.json index 5134dff233..fe73288033 100644 --- a/contributors.json +++ b/contributors.json @@ -1,5 +1,5 @@ { - "ℹ️": "Auto-generated file. Run `pnpm run list-contributors` to regenerate.", + "⚠️": "Auto-generated file. Run `pnpm run update-contributors` to regenerate.", "contributors": [ { "name": "eliandoran", @@ -23,15 +23,6 @@ "fullName": "Jon Fuller", "url": "https://github.com/perfectra1n" }, - { - "name": "pano9000", - "fullName": "Panagiotis Papadopoulos", - "url": "https://github.com/pano9000" - }, - { - "name": "SiriusXT", - "url": "https://github.com/SiriusXT" - }, { "name": "JYC333", "url": "https://github.com/JYC333" diff --git a/package.json b/package.json index 17254a5179..cd3505b422 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "dev:linter-fix": "cross-env NODE_OPTIONS=--max_old_space_size=4096 eslint . --fix", "postinstall": "tsx scripts/electron-rebuild.mts && pnpm prepare", "prepare": "pnpm run --filter pdfjs-viewer --filter share-theme build && pnpm run --filter web-clipper postinstall", - "list-contributors": "tsx ./scripts/list-contributors.ts" + "update-contributors": "tsx ./scripts/update-contributors.ts" }, "private": true, "devDependencies": { diff --git a/scripts/list-contributors.ts b/scripts/update-contributors.ts similarity index 98% rename from scripts/list-contributors.ts rename to scripts/update-contributors.ts index c58766d5f6..62a209b066 100644 --- a/scripts/list-contributors.ts +++ b/scripts/update-contributors.ts @@ -205,7 +205,7 @@ function updateContributorsJson(developers: ContributorInfo[]) { } const output = { - "ℹ️": "Auto-generated file. Run `pnpm run list-contributors` to regenerate.", + "⚠️": "Auto-generated file. Run `pnpm run update-contributors` to regenerate.", contributors }; writeFileSync(CONTRIBUTORS_PATH, JSON.stringify(output, null, 4) + "\n");