Merge remote-tracking branch 'origin/develop' into client_vite

This commit is contained in:
Elian Doran 2025-05-19 16:42:11 +03:00
commit 41533598fd
No known key found for this signature in database
16 changed files with 461 additions and 718 deletions

View File

@ -38,10 +38,10 @@
"@playwright/test": "1.52.0", "@playwright/test": "1.52.0",
"@stylistic/eslint-plugin": "4.2.0", "@stylistic/eslint-plugin": "4.2.0",
"@types/express": "5.0.1", "@types/express": "5.0.1",
"@types/node": "22.15.18", "@types/node": "22.15.19",
"@types/yargs": "17.0.33", "@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.1.3", "@vitest/coverage-v8": "3.1.3",
"eslint": "9.26.0", "eslint": "9.27.0",
"eslint-plugin-simple-import-sort": "12.1.1", "eslint-plugin-simple-import-sort": "12.1.1",
"esm": "3.2.25", "esm": "3.2.25",
"jsdoc": "4.0.4", "jsdoc": "4.0.4",

View File

@ -10,7 +10,7 @@
"url": "https://github.com/TriliumNext/Notes" "url": "https://github.com/TriliumNext/Notes"
}, },
"dependencies": { "dependencies": {
"@eslint/js": "9.26.0", "@eslint/js": "9.27.0",
"@excalidraw/excalidraw": "0.18.0", "@excalidraw/excalidraw": "0.18.0",
"@fullcalendar/core": "6.1.17", "@fullcalendar/core": "6.1.17",
"@fullcalendar/daygrid": "6.1.17", "@fullcalendar/daygrid": "6.1.17",

View File

@ -1637,7 +1637,9 @@ div.find-replace-widget div.find-widget-found-wrapper > span {
#right-pane .toc li, #right-pane .toc li,
#right-pane .highlights-list li { #right-pane .highlights-list li {
padding: 2px 8px; padding-top: 2px;
padding-right: 8px;
padding-bottom: 2px;
border-radius: 4px; border-radius: 4px;
text-align: unset; text-align: unset;
transition: transition:

View File

@ -35,12 +35,8 @@ const TPL = /*html*/`<div class="toc-widget">
.toc ol { .toc ol {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
padding-left: 20px;
transition: max-height 0.3s ease;
}
.toc > ol {
padding-left: 0px; padding-left: 0px;
transition: max-height 0.3s ease;
} }
.toc li.collapsed + ol { .toc li.collapsed + ol {
@ -51,8 +47,8 @@ const TPL = /*html*/`<div class="toc-widget">
content: ""; content: "";
position: absolute; position: absolute;
height: 100%; height: 100%;
left: 17px;
border-left: 1px solid var(--main-border-color); border-left: 1px solid var(--main-border-color);
z-index: 10;
} }
.toc li { .toc li {
@ -67,11 +63,35 @@ const TPL = /*html*/`<div class="toc-widget">
hyphens: auto; hyphens: auto;
} }
.toc > ol {
--toc-depth-level: 1;
}
.toc > ol > ol {
--toc-depth-level: 2;
}
.toc > ol > ol > ol {
--toc-depth-level: 3;
}
.toc > ol > ol > ol > ol {
--toc-depth-level: 4;
}
.toc > ol > ol > ol > ol > ol {
--toc-depth-level: 5;
}
.toc > ol ol::before {
left: calc((var(--toc-depth-level) - 2) * 20px + 14px);
}
.toc li {
padding-left: calc((var(--toc-depth-level) - 1) * 20px + 4px);
}
.toc li .collapse-button { .toc li .collapse-button {
display: flex; display: flex;
position: relative; position: relative;
width: 20px; width: 21px;
height: 20px; height: 21px;
flex-shrink: 0; flex-shrink: 0;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -83,12 +103,12 @@ const TPL = /*html*/`<div class="toc-widget">
} }
.toc li .item-content { .toc li .item-content {
margin-left: 28px; margin-left: 25px;
flex: 1; flex: 1;
} }
.toc li .collapse-button + .item-content { .toc li .collapse-button + .item-content {
margin-left: 8px; margin-left: 4px;
} }
.toc li:hover { .toc li:hover {
@ -316,10 +336,9 @@ export default class TocWidget extends RightPanelWidget {
// //
const headingText = await this.replaceMathTextWithKatax(m[2]); const headingText = await this.replaceMathTextWithKatax(m[2]);
const $itemContent = $('<div class="item-content">').html(headingText).on("click", () => { const $itemContent = $('<div class="item-content">').html(headingText);
this.jumpToHeading(headingIndex); const $li = $("<li>").append($itemContent)
}); .on("click", () => this.jumpToHeading(headingIndex));
const $li = $("<li>").append($itemContent);
$ols[$ols.length - 1].append($li); $ols[$ols.length - 1].append($li);
headingCount = headingIndex; headingCount = headingIndex;
$previousLi = $li; $previousLi = $li;
@ -400,7 +419,8 @@ export default class TocWidget extends RightPanelWidget {
$previousLi.removeClass("collapsed"); $previousLi.removeClass("collapsed");
} }
$collapseButton.on("click", () => { $collapseButton.on("click", (event) => {
event.stopPropagation();
if ($previousLi.hasClass("animating")) return; if ($previousLi.hasClass("animating")) return;
const willCollapse = !$previousLi.hasClass("collapsed"); const willCollapse = !$previousLi.hasClass("collapsed");
$previousLi.addClass("animating"); $previousLi.addClass("animating");

View File

@ -88,7 +88,7 @@
"multer": "1.4.5-lts.2", "multer": "1.4.5-lts.2",
"normalize-strings": "1.1.1", "normalize-strings": "1.1.1",
"ollama": "0.5.15", "ollama": "0.5.15",
"openai": "4.98.0", "openai": "4.100.0",
"rand-token": "1.0.1", "rand-token": "1.0.1",
"safe-compare": "1.1.4", "safe-compare": "1.1.4",
"sanitize-filename": "1.6.3", "sanitize-filename": "1.6.3",

View File

@ -1,6 +1,7 @@
import { beforeAll } from "vitest"; import { beforeAll } from "vitest";
import i18next from "i18next"; import i18next from "i18next";
import { join } from "path"; import { join } from "path";
import dayjs from "dayjs";
beforeAll(async () => { beforeAll(async () => {
// Initialize the translations manually to avoid any side effects. // Initialize the translations manually to avoid any side effects.
@ -15,4 +16,8 @@ beforeAll(async () => {
loadPath: join(__dirname, "../src/assets/translations/{{lng}}/{{ns}}.json") loadPath: join(__dirname, "../src/assets/translations/{{lng}}/{{ns}}.json")
} }
}); });
// Initialize dayjs
await import("dayjs/locale/en.js");
dayjs.locale("en");
}); });

View File

@ -4,7 +4,6 @@ import favicon from "serve-favicon";
import cookieParser from "cookie-parser"; import cookieParser from "cookie-parser";
import helmet from "helmet"; import helmet from "helmet";
import compression from "compression"; import compression from "compression";
import sessionParser from "./routes/session_parser.js";
import config from "./services/config.js"; import config from "./services/config.js";
import utils, { getResourceDir } from "./services/utils.js"; import utils, { getResourceDir } from "./services/utils.js";
import assets from "./routes/assets.js"; import assets from "./routes/assets.js";
@ -111,6 +110,8 @@ export default async function buildApp() {
app.use(`/manifest.webmanifest`, express.static(path.join(publicAssetsDir, "manifest.webmanifest"))); app.use(`/manifest.webmanifest`, express.static(path.join(publicAssetsDir, "manifest.webmanifest")));
app.use(`/robots.txt`, express.static(path.join(publicAssetsDir, "robots.txt"))); app.use(`/robots.txt`, express.static(path.join(publicAssetsDir, "robots.txt")));
app.use(`/icon.png`, express.static(path.join(publicAssetsDir, "icon.png"))); app.use(`/icon.png`, express.static(path.join(publicAssetsDir, "icon.png")));
const sessionParser = (await import("./routes/session_parser.js")).default;
app.use(sessionParser); app.use(sessionParser);
app.use(favicon(path.join(assetsDir, "icon.ico"))); app.use(favicon(path.join(assetsDir, "icon.ico")));

View File

@ -1,5 +1,3 @@
<p><strong>Note: This feature has not been merged yet, so it is not available.</strong>
</p>
<p>Multi-factor authentication (MFA) is a security process that requires <p>Multi-factor authentication (MFA) is a security process that requires
users to provide two or more verification factors to gain access to a system, users to provide two or more verification factors to gain access to a system,
application, or account. This adds an extra layer of protection beyond application, or account. This adds an extra layer of protection beyond
@ -7,80 +5,60 @@
<p>By requiring more than one verification method, MFA helps reduce the risk <p>By requiring more than one verification method, MFA helps reduce the risk
of unauthorized access, even if someone has obtained your password. Its of unauthorized access, even if someone has obtained your password. Its
highly recommended for securing sensitive information stored in your notes.</p> highly recommended for securing sensitive information stored in your notes.</p>
<p>Warning! OpenID and TOTP cannot be both used at the same time!</p> <aside
<h2>Log in with your Google Account with OpenID!</h2> class="admonition warning">
<p>OpenID is a standardized way to let you log into websites using an account <p>OpenID and TOTP cannot be both used at the same time!</p>
</aside>
<h2>Log in with your Google Account with OpenID!</h2>
<p>OpenID is a standardized way to let you log into websites using an account
from another service, like Google, to verify your identity.</p> from another service, like Google, to verify your identity.</p>
<h2>Why Time-based One Time Passwords?</h2> <h2>Why Time-based One Time Passwords?</h2>
<p>TOTP (Time-Based One-Time Password) is a security feature that generates <p>TOTP (Time-Based One-Time Password) is a security feature that generates
a unique, temporary code on your device, like a smartphone, which changes a unique, temporary code on your device, like a smartphone, which changes
every 30 seconds. You use this code, along with your password, to log into every 30 seconds. You use this code, along with your password, to log into
your account, making it much harder for anyone else to access them.</p> your account, making it much harder for anyone else to access them.</p>
<h2>Setup</h2> <h2>Setup</h2>
<h3>TOTP</h3> <p>MFA can only be set up on a server instance.</p>
<ol> <aside class="admonition note">
<li> <p>When Multi-Factor Authentication (MFA) is enabled on a server instance,
<p>Start Trilium Notes normally.</p> a new desktop instance may fail to sync with it. As a temporary workaround,
</li> you can disable MFA to complete the initial sync, then re-enable MFA afterward.
<li> This issue will be addressed in a future release.</p>
<p>Go to "Menu" -&gt; "Options" -&gt; "MFA"</p> </aside>
</li> <h3>TOTP</h3>
<li> <ol>
<p>Click the "Generate TOTP Secret" button</p> <li>Go to "Menu" -&gt; "Options" -&gt; "MFA"</li>
</li> <li>Click the “Enable Multi-Factor Authentication” checkbox if not checked</li>
<li> <li>Choose “Time-Based One-Time Password (TOTP)” under MFA Method</li>
<p>Copy the generated secret to your authentication app/extension</p> <li>Click the "Generate TOTP Secret" button</li>
</li> <li>Copy the generated secret to your authentication app/extension</li>
<li> <li>Click the "Generate Recovery Codes" button</li>
<p>Set an environment variable "TOTP_SECRET" as the generated secret. Environment <li>Save the recovery codes. Recovery codes can be used once in place of the
variables can be set with a .env file in the root directory, by defining
them in the command line, or with a docker container.</p><pre><code class="language-text-x-trilium-auto"># .env in the project root directory
TOTP_ENABLED="true"
TOTP_SECRET="secret"</code></pre><pre><code class="language-text-x-trilium-auto"># Terminal/CLI
export TOTP_ENABLED="true"
export TOTP_SECRET="secret"</code></pre><pre><code class="language-text-x-trilium-auto"># Docker
docker run -p 8080:8080 -v ~/trilium-data:/home/node/trilium-data -e TOTP_ENABLED="true" -e TOTP_SECRET="secret" triliumnext/notes:[VERSION]</code></pre>
</li>
<li>
<p>Restart Trilium</p>
</li>
<li>
<p>Go to "Options" -&gt; "MFA"</p>
</li>
<li>
<p>Click the "Generate Recovery Codes" button</p>
</li>
<li>
<p>Save the recovery codes. Recovery codes can be used once in place of the
TOTP if you loose access to your authenticator. After a rerecovery code TOTP if you loose access to your authenticator. After a rerecovery code
is used, it will show the unix timestamp when it was used in the MFA options is used, it will show the unix timestamp when it was used in the MFA options
tab.</p> tab.</li>
</li> <li>Re-login will be required after TOTP setup is finished (After you refreshing
<li> the page).</li>
<p>Load the secret into an authentication app like google authenticator</p> </ol>
</li> <h3>OpenID</h3>
</ol> <aside class="admonition note">
<h3>OpenID</h3> <p>Currently only compatible with Google. Other services like Authentik and
<p><em>Currently only compatible with Google. Other services like Authentik and Auth0 are planned on being added.</em> Auth0 are planned on being added.</p>
</p> </aside>
<p>In order to setup OpenID, you will need to setup a authentication provider. <p>In order to setup OpenID, you will need to setup a authentication provider.
This requires a bit of extra setup. Follow <a href="https://developers.google.com/identity/openid-connect/openid-connect">these instructions</a> to This requires a bit of extra setup. Follow <a href="https://developers.google.com/identity/openid-connect/openid-connect">these instructions</a> to
setup an OpenID service through google.</p> setup an OpenID service through google.</p>
<p>Set an environment variable "SSO_ENABLED" to true and add the client ID <ol>
and secret you obtained from google. Environment variables can be set with <li>Set the <code>oauthBaseUrl</code>, <code>oauthClientId</code> and <code>oauthClientSecret</code> in
a .env file in the root directory, by defining them in the command line, the <code>config.ini</code> file (check&nbsp;<a class="reference-link" href="#root/_help_Gzjqa934BdH4">Configuration (config.ini or environment variables)</a>&nbsp;for
or with a docker container.</p> more information).
<h4>.env File</h4><pre><code class="language-text-x-trilium-auto"># .env in the project root directory <ol>
SSO_ENABLED="true" <li>You can also setup through environment variables (<code>TRILIUM_OAUTH_BASE_URL</code>, <code>TRILIUM_OAUTH_CLIENT_ID</code> and <code>TRILIUM_OAUTH_CLIENT_SECRET</code>).</li>
BASE_URL="http://localhost:8080" </ol>
CLIENT_ID= </li>
SECRET=</code></pre> <li>Restart the server</li>
<h4>Environment variable (linux)</h4><pre><code class="language-text-x-trilium-auto">export SSO_ENABLED="true" <li>Go to "Menu" -&gt; "Options" -&gt; "MFA"</li>
export BASE_URL="http://localhost:8080" <li>Click the “Enable Multi-Factor Authentication” checkbox if not checked</li>
export CLIENT_ID= <li>Choose “OAuth/OpenID” under MFA Method</li>
export SECRET=</code></pre> <li>Refresh the page and login through OpenID provider</li>
<h4>Docker</h4><pre><code class="language-text-x-trilium-auto">docker run -d -p 8080:8080 -v ~/trilium-data:/home/node/trilium-data -e SSO_ENABLED="true" -e BASE_URL="http://localhost:8080" -e CLIENT_ID= -e SECRET= triliumnext/notes:[VERSION]</code></pre> </ol>
<p>After you restart Trilium Notes, you will be redirected to Google's account
selection page. Login to an account and Trilium Next will bind to that
account, allowing you to login with it.</p>
<p>You can now login using your google account.</p>

View File

@ -40,7 +40,7 @@
<h2>Color schemes</h2> <h2>Color schemes</h2>
<p>Since Trilium 0.94.0 the colors of code notes can be customized by going&nbsp; <p>Since Trilium 0.94.0 the colors of code notes can be customized by going&nbsp;
<a <a
class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_4TIF1oA4VQRO">Options</a>&nbsp;→ Code Notes and looking for the <em>Appearance</em> section.</p> class="reference-link" href="#root/_help_4TIF1oA4VQRO">Options</a>&nbsp;→ Code Notes and looking for the <em>Appearance</em> section.</p>
<aside <aside
class="admonition note"> class="admonition note">
<p><strong>Why are there only a few themes whereas the code block themes for text notes have a lot?</strong> <p><strong>Why are there only a few themes whereas the code block themes for text notes have a lot?</strong>

View File

@ -36,11 +36,11 @@ export async function initializeTranslations() {
} catch (err) { } catch (err) {
console.warn(`Could not load locale ${dayjsLocale}`, err); console.warn(`Could not load locale ${dayjsLocale}`, err);
} }
dayjs.locale(dayjsLocale);
} }
export function ordinal(date: Dayjs) { export function ordinal(date: Dayjs) {
return dayjs(date) return dayjs(date)
.locale(dayjsLocale)
.format("Do"); .format("Do");
} }

View File

@ -1,6 +1,4 @@
#!/usr/bin/env node #!/usr/bin/env node
import sessionParser from "./routes/session_parser.js";
import fs from "fs"; import fs from "fs";
import http from "http"; import http from "http";
import https from "https"; import https from "https";
@ -79,6 +77,7 @@ async function startTrilium() {
const httpServer = startHttpServer(app); const httpServer = startHttpServer(app);
const sessionParser = (await import("./routes/session_parser.js")).default;
ws.init(httpServer, sessionParser as any); // TODO: Not sure why session parser is incompatible. ws.init(httpServer, sessionParser as any); // TODO: Not sure why session parser is incompatible.
if (utils.isElectron) { if (utils.isElectron) {

View File

@ -666,6 +666,13 @@
"type": "text", "type": "text",
"mime": "text/markdown", "mime": "text/markdown",
"attributes": [ "attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "Gzjqa934BdH4",
"isInheritable": false,
"position": 10
},
{ {
"type": "label", "type": "label",
"name": "shareAlias", "name": "shareAlias",

View File

@ -1,11 +1,10 @@
# Multi-Factor Authentication # Multi-Factor Authentication
**Note: This feature has not been merged yet, so it is not available.**
Multi-factor authentication (MFA) is a security process that requires users to provide two or more verification factors to gain access to a system, application, or account. This adds an extra layer of protection beyond just using a password. Multi-factor authentication (MFA) is a security process that requires users to provide two or more verification factors to gain access to a system, application, or account. This adds an extra layer of protection beyond just using a password.
By requiring more than one verification method, MFA helps reduce the risk of unauthorized access, even if someone has obtained your password. Its highly recommended for securing sensitive information stored in your notes. By requiring more than one verification method, MFA helps reduce the risk of unauthorized access, even if someone has obtained your password. Its highly recommended for securing sensitive information stored in your notes.
Warning! OpenID and TOTP cannot be both used at the same time! > [!WARNING]
> OpenID and TOTP cannot be both used at the same time!
## Log in with your Google Account with OpenID! ## Log in with your Google Account with OpenID!
@ -17,69 +16,33 @@ TOTP (Time-Based One-Time Password) is a security feature that generates a uniqu
## Setup ## Setup
MFA can only be set up on a server instance.
> [!NOTE]
> When Multi-Factor Authentication (MFA) is enabled on a server instance, a new desktop instance may fail to sync with it. As a temporary workaround, you can disable MFA to complete the initial sync, then re-enable MFA afterward. This issue will be addressed in a future release.
### TOTP ### TOTP
1. Start Trilium Notes normally. 1. Go to "Menu" -> "Options" -> "MFA"
2. Go to "Menu" -> "Options" -> "MFA" 2. Click the “Enable Multi-Factor Authentication” checkbox if not checked
3. Click the "Generate TOTP Secret" button 3. Choose “Time-Based One-Time Password (TOTP)” under MFA Method
4. Copy the generated secret to your authentication app/extension 4. Click the "Generate TOTP Secret" button
5. Set an environment variable "TOTP\_SECRET" as the generated secret. Environment variables can be set with a .env file in the root directory, by defining them in the command line, or with a docker container. 5. Copy the generated secret to your authentication app/extension
6. Click the "Generate Recovery Codes" button
``` 7. Save the recovery codes. Recovery codes can be used once in place of the TOTP if you loose access to your authenticator. After a rerecovery code is used, it will show the unix timestamp when it was used in the MFA options tab.
# .env in the project root directory 8. Re-login will be required after TOTP setup is finished (After you refreshing the page).
TOTP_ENABLED="true"
TOTP_SECRET="secret"
```
```
# Terminal/CLI
export TOTP_ENABLED="true"
export TOTP_SECRET="secret"
```
```
# Docker
docker run -p 8080:8080 -v ~/trilium-data:/home/node/trilium-data -e TOTP_ENABLED="true" -e TOTP_SECRET="secret" triliumnext/notes:[VERSION]
```
6. Restart Trilium
7. Go to "Options" -> "MFA"
8. Click the "Generate Recovery Codes" button
9. Save the recovery codes. Recovery codes can be used once in place of the TOTP if you loose access to your authenticator. After a rerecovery code is used, it will show the unix timestamp when it was used in the MFA options tab.
10. Load the secret into an authentication app like google authenticator
### OpenID ### OpenID
_Currently only compatible with Google. Other services like Authentik and Auth0 are planned on being added._ > [!NOTE]
> Currently only compatible with Google. Other services like Authentik and Auth0 are planned on being added.
In order to setup OpenID, you will need to setup a authentication provider. This requires a bit of extra setup. Follow [these instructions](https://developers.google.com/identity/openid-connect/openid-connect) to setup an OpenID service through google. In order to setup OpenID, you will need to setup a authentication provider. This requires a bit of extra setup. Follow [these instructions](https://developers.google.com/identity/openid-connect/openid-connect) to setup an OpenID service through google.
Set an environment variable "SSO\_ENABLED" to true and add the client ID and secret you obtained from google. Environment variables can be set with a .env file in the root directory, by defining them in the command line, or with a docker container. 1. Set the `oauthBaseUrl`, `oauthClientId` and `oauthClientSecret` in the `config.ini` file (check <a class="reference-link" href="../../Advanced%20Usage/Configuration%20(config.ini%20or%20e.md">Configuration (config.ini or environment variables)</a> for more information).
1. You can also setup through environment variables (`TRILIUM_OAUTH_BASE_URL`, `TRILIUM_OAUTH_CLIENT_ID` and `TRILIUM_OAUTH_CLIENT_SECRET`).
#### .env File 2. Restart the server
3. Go to "Menu" -> "Options" -> "MFA"
``` 4. Click the “Enable Multi-Factor Authentication” checkbox if not checked
# .env in the project root directory 5. Choose “OAuth/OpenID” under MFA Method
SSO_ENABLED="true" 6. Refresh the page and login through OpenID provider
BASE_URL="http://localhost:8080"
CLIENT_ID=
SECRET=
```
#### Environment variable (linux)
```
export SSO_ENABLED="true"
export BASE_URL="http://localhost:8080"
export CLIENT_ID=
export SECRET=
```
#### Docker
```
docker run -d -p 8080:8080 -v ~/trilium-data:/home/node/trilium-data -e SSO_ENABLED="true" -e BASE_URL="http://localhost:8080" -e CLIENT_ID= -e SECRET= triliumnext/notes:[VERSION]
```
After you restart Trilium Notes, you will be redirected to Google's account selection page. Login to an account and Trilium Next will bind to that account, allowing you to login with it.
You can now login using your google account.

View File

@ -46,7 +46,7 @@
"@swc/helpers": "~0.5.11", "@swc/helpers": "~0.5.11",
"@triliumnext/server": "workspace:*", "@triliumnext/server": "workspace:*",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/node": "22.15.18", "@types/node": "22.15.19",
"@vitest/coverage-v8": "^3.0.5", "@vitest/coverage-v8": "^3.0.5",
"@vitest/ui": "^3.0.0", "@vitest/ui": "^3.0.0",
"chalk": "5.4.1", "chalk": "5.4.1",

View File

@ -61,6 +61,6 @@
"@ssddanbrown/codemirror-lang-twig": "1.0.0", "@ssddanbrown/codemirror-lang-twig": "1.0.0",
"codemirror-lang-hcl": "0.1.0", "codemirror-lang-hcl": "0.1.0",
"codemirror-lang-mermaid": "0.5.0", "codemirror-lang-mermaid": "0.5.0",
"eslint-linter-browserify": "9.26.0" "eslint-linter-browserify": "9.27.0"
} }
} }

874
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff