diff --git a/README.md b/README.md
index 9f7fba27a..02a9c969b 100644
--- a/README.md
+++ b/README.md
@@ -4,15 +4,47 @@
[English](./README.md) | [Chinese](./docs/README-ZH_CN.md) | [Russian](./docs/README.ru.md) | [Japanese](./docs/README.ja.md) | [Italian](./docs/README.it.md) | [Spanish](./docs/README.es.md)
-TriliumNext Notes is an open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases.
+TriliumNext Notes is a free and open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases.
See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for quick overview:
+## 🎁 Features
+
+* Notes can be arranged into arbitrarily deep tree. Single note can be placed into multiple places in the tree (see [cloning](https://triliumnext.github.io/Docs/Wiki/cloning-notes))
+* Rich WYSIWYG note editor including e.g. tables, images and [math](https://triliumnext.github.io/Docs/Wiki/text-notes) with markdown [autoformat](https://triliumnext.github.io/Docs/Wiki/text-notes#autoformat)
+* Support for editing [notes with source code](https://triliumnext.github.io/Docs/Wiki/code-notes), including syntax highlighting
+* Fast and easy [navigation between notes](https://triliumnext.github.io/Docs/Wiki/note-navigation), full text search and [note hoisting](https://triliumnext.github.io/Docs/Wiki/note-hoisting)
+* Seamless [note versioning](https://triliumnext.github.io/Docs/Wiki/note-revisions)
+* Note [attributes](https://triliumnext.github.io/Docs/Wiki/attributes) can be used for note organization, querying and advanced [scripting](https://triliumnext.github.io/Docs/Wiki/scripts)
+* UI available in English, German, Spanish, French, Romanian, and Chinese (simplified and traditional)
+* Direct [OpenID and TOTP integration](.docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Server%20Installation/Multi-Factor%20Authentication.md") for more secure login
+* [Synchronization](https://triliumnext.github.io/Docs/Wiki/synchronization) with self-hosted sync server
+ * there's a [3rd party service for hosting synchronisation server](https://trilium.cc/paid-hosting)
+* [Sharing](https://triliumnext.github.io/Docs/Wiki/sharing) (publishing) notes to public internet
+* Strong [note encryption](https://triliumnext.github.io/Docs/Wiki/protected-notes) with per-note granularity
+* Sketching diagrams, based on [Excalidraw](https://excalidraw.com/) (note type "canvas")
+* [Relation maps](https://triliumnext.github.io/Docs/Wiki/relation-map) and [link maps](https://triliumnext.github.io/Docs/Wiki/link-map) for visualizing notes and their relations
+* Mind maps, based on [Mind Elixir](https://docs.mind-elixir.com/)
+* [Geo maps](./docs/User%20Guide/User%20Guide/Note%20Types/Geo%20Map.md) with location pins and GPX tracks
+* [Scripting](https://triliumnext.github.io/Docs/Wiki/scripts) - see [Advanced showcases](https://triliumnext.github.io/Docs/Wiki/advanced-showcases)
+* [REST API](https://triliumnext.github.io/Docs/Wiki/etapi) for automation
+* Scales well in both usability and performance upwards of 100 000 notes
+* Touch optimized [mobile frontend](https://triliumnext.github.io/Docs/Wiki/mobile-frontend) for smartphones and tablets
+* Built-in [dark theme](https://triliumnext.github.io/Docs/Wiki/themes), support for user themes
+* [Evernote](https://triliumnext.github.io/Docs/Wiki/evernote-import) and [Markdown import & export](https://triliumnext.github.io/Docs/Wiki/markdown)
+* [Web Clipper](https://triliumnext.github.io/Docs/Wiki/web-clipper) for easy saving of web content
+* Customizable UI (sidebar buttons, user-defined widgets, ...)
+
+✨ Check out the following third-party resources/communities for more TriliumNext related goodies:
+
+- [awesome-trilium](https://github.com/Nriver/awesome-trilium) for 3rd party themes, scripts, plugins and more.
+- [TriliumRocks!](https://trilium.rocks/) for tutorials, guides, and much more.
+
## ⚠️ Why TriliumNext?
-[The original Trilium project is in maintenance mode](https://github.com/zadam/trilium/issues/4620)
+[The original Trilium project is in maintenance mode](https://github.com/zadam/trilium/issues/4620).
### Migrating from Trilium?
@@ -20,7 +52,7 @@ There are no special migration steps to migrate from a zadam/Trilium instance to
Versions up to and including [v0.90.4](https://github.com/TriliumNext/Notes/releases/tag/v0.90.4) are compatible with the latest zadam/trilium version of [v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Any later versions of TriliumNext have their sync versions incremented.
-## Documentation
+## 📖 Documentation
We're currently in the progress of moving the documentation to in-app (hit the `F1` key within Trilium). As a result, there may be some missing parts until we've completed the migration. If you'd prefer to navigate through the documentation within GitHub, you can navigate the [User Guide](./docs/User%20Guide/User%20Guide/) documentation.
@@ -29,55 +61,40 @@ Below are some quick links for your convenience to navigate the documentation:
- [Docker installation](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation/1.%20Installing%20the%20server/Using%20Docker.md)
- [Upgrading TriliumNext](./docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Upgrading%20TriliumNext.md)
- [Concepts and Features - Note](./docs/User%20Guide/User%20Guide/Basic%20Concepts%20and%20Features/Notes.md)
+- [Patterns of personal knowledge base](https://triliumnext.github.io/Docs/Wiki/patterns-of-personal-knowledge)
+Until we finish reorganizing the documentation, you may also want to [browse the old documentation](https://triliumnext.github.io/Docs).
## 💬 Discuss with us
Feel free to join our official conversations. We would love to hear what features, suggestions, or issues you may have!
-- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synchronous discussions)
+- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synchronous discussions.)
- The `General` Matrix room is also bridged to [XMPP](xmpp:discuss@trilium.thisgreat.party?join)
-- [Github Discussions](https://github.com/TriliumNext/Notes/discussions) (For Asynchronous discussions)
-- [Wiki](https://triliumnext.github.io/Docs/) (For common how-to questions and user guides)
-
-## 🎁 Features
-
-* Notes can be arranged into arbitrarily deep tree. Single note can be placed into multiple places in the tree (see [cloning](https://triliumnext.github.io/Docs/Wiki/cloning-notes))
-* Rich WYSIWYG note editing including e.g. tables, images and [math](https://triliumnext.github.io/Docs/Wiki/text-notes) with markdown [autoformat](https://triliumnext.github.io/Docs/Wiki/text-notes#autoformat)
-* Support for editing [notes with source code](https://triliumnext.github.io/Docs/Wiki/code-notes), including syntax highlighting
-* Fast and easy [navigation between notes](https://triliumnext.github.io/Docs/Wiki/note-navigation), full text search and [note hoisting](https://triliumnext.github.io/Docs/Wiki/note-hoisting)
-* Seamless [note versioning](https://triliumnext.github.io/Docs/Wiki/note-revisions)
-* Note [attributes](https://triliumnext.github.io/Docs/Wiki/attributes) can be used for note organization, querying and advanced [scripting](https://triliumnext.github.io/Docs/Wiki/scripts)
-* Direct OpenID and TOTP integration for more secure login
-* [Synchronization](https://triliumnext.github.io/Docs/Wiki/synchronization) with self-hosted sync server
- * there's a [3rd party service for hosting synchronisation server](https://trilium.cc/paid-hosting)
-* [Sharing](https://triliumnext.github.io/Docs/Wiki/sharing) (publishing) notes to public internet
-* Strong [note encryption](https://triliumnext.github.io/Docs/Wiki/protected-notes) with per-note granularity
-* Sketching diagrams with built-in Excalidraw (note type "canvas")
-* [Relation maps](https://triliumnext.github.io/Docs/Wiki/relation-map) and [link maps](https://triliumnext.github.io/Docs/Wiki/link-map) for visualizing notes and their relations
-* [Scripting](https://triliumnext.github.io/Docs/Wiki/scripts) - see [Advanced showcases](https://triliumnext.github.io/Docs/Wiki/advanced-showcases)
-* [REST API](https://triliumnext.github.io/Docs/Wiki/etapi) for automation
-* Scales well in both usability and performance upwards of 100 000 notes
-* Touch optimized [mobile frontend](https://triliumnext.github.io/Docs/Wiki/mobile-frontend) for smartphones and tablets
-* [Night theme](https://triliumnext.github.io/Docs/Wiki/themes)
-* [Evernote](https://triliumnext.github.io/Docs/Wiki/evernote-import) and [Markdown import & export](https://triliumnext.github.io/Docs/Wiki/markdown)
-* [Web Clipper](https://triliumnext.github.io/Docs/Wiki/web-clipper) for easy saving of web content
-
-✨ Check out the following third-party resources/communities for more TriliumNext related goodies:
-
-- [awesome-trilium](https://github.com/Nriver/awesome-trilium) for 3rd party themes, scripts, plugins and more.
-- [TriliumRocks!](https://trilium.rocks/) for tutorials, guides, and much more.
+- [Github Discussions](https://github.com/TriliumNext/Notes/discussions) (For asynchronous discussions.)
+- [Github Issues](https://github.com/TriliumNext/Notes/issues) (For bug reports and feature requests.)
## 🏗 Installation
-### Desktop
+### Windows / MacOS
-To use TriliumNext on your desktop machine (Linux, MacOS, and Windows) you have a few options:
+Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the `trilium` executable.
-* Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the `trilium` executable.
-* Access TriliumNext via the web interface of a server installation (see below)
- * Currently only the latest versions of Chrome & Firefox are supported (and tested).
-* TriliumNext is also provided as a Flatpak, but not yet published on FlatHub.
+### Linux
+
+If your distribution is listed in the table below, use your distribution's package.
+
+[](https://repology.org/project/trilium-next-desktop/versions)
+
+You may also download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the `trilium` executable.
+
+TriliumNext is also provided as a Flatpak, but not yet published on FlatHub.
+
+### Browser (any OS)
+
+If you use a server installation (see below), you can directly access the web interface (which is almost identical to the desktop app).
+
+Currently only the latest versions of Chrome & Firefox are supported (and tested).
### Mobile
@@ -91,11 +108,6 @@ See issue https://github.com/TriliumNext/Notes/issues/72 for more information on
To install TriliumNext on your own server (including via Docker from [Dockerhub](https://hub.docker.com/r/triliumnext/notes)) follow [the server installation docs](https://triliumnext.github.io/Docs/Wiki/server-installation).
-## 📝 Documentation
-
-[See wiki for complete list of documentation pages.](https://triliumnext.github.io/Docs)
-
-You can also read [Patterns of personal knowledge base](https://triliumnext.github.io/Docs/Wiki/patterns-of-personal-knowledge) to get some inspiration on how you might use TriliumNext.
## 💻 Contribute
@@ -150,4 +162,6 @@ Support for the TriliumNext organization will be possible in the near future. Fo
## 🔑 License
+Copyright 2017-2025 zadam, Elian Doran, and other contributors
+
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
diff --git a/_regroup/package.json b/_regroup/package.json
index 3ba71e7fe..3420f8a0c 100644
--- a/_regroup/package.json
+++ b/_regroup/package.json
@@ -38,9 +38,9 @@
"@playwright/test": "1.52.0",
"@stylistic/eslint-plugin": "4.2.0",
"@types/express": "5.0.1",
- "@types/node": "22.15.18",
+ "@types/node": "22.15.19",
"@types/yargs": "17.0.33",
- "@vitest/coverage-v8": "3.1.3",
+ "@vitest/coverage-v8": "3.1.4",
"eslint": "9.27.0",
"eslint-plugin-simple-import-sort": "12.1.1",
"esm": "3.2.25",
diff --git a/apps/client/package.json b/apps/client/package.json
index db36112dc..16cb5fb4b 100644
--- a/apps/client/package.json
+++ b/apps/client/package.json
@@ -31,7 +31,7 @@
"draggabilly": "3.0.0",
"force-graph": "1.49.6",
"globals": "16.1.0",
- "i18next": "25.1.3",
+ "i18next": "25.2.0",
"i18next-http-backend": "3.0.2",
"jquery": "3.7.1",
"jquery-hotkeys": "0.2.2",
@@ -41,7 +41,7 @@
"leaflet": "1.9.4",
"leaflet-gpx": "2.2.0",
"mark.js": "8.11.1",
- "marked": "15.0.11",
+ "marked": "15.0.12",
"mermaid": "11.6.0",
"mind-elixir": "4.5.2",
"panzoom": "9.4.3",
@@ -55,7 +55,7 @@
"@ckeditor/ckeditor5-inspector": "4.1.0",
"@types/bootstrap": "5.2.10",
"@types/jquery": "3.5.32",
- "@types/leaflet": "1.9.17",
+ "@types/leaflet": "1.9.18",
"@types/leaflet-gpx": "1.3.7",
"@types/react": "19.1.4",
"@types/react-dom": "19.1.5",
diff --git a/apps/client/src/components/app_context.ts b/apps/client/src/components/app_context.ts
index 1855876d3..b64678011 100644
--- a/apps/client/src/components/app_context.ts
+++ b/apps/client/src/components/app_context.ts
@@ -283,6 +283,9 @@ export type CommandMappings = {
type EventMappings = {
initialRenderComplete: {};
frocaReloaded: {};
+ setLeftPaneVisibility: {
+ leftPaneVisible: boolean | null;
+ }
protectedSessionStarted: {};
notesReloaded: {
noteIds: string[];
diff --git a/apps/client/src/components/root_command_executor.ts b/apps/client/src/components/root_command_executor.ts
index 1e16fae81..8e7df9494 100644
--- a/apps/client/src/components/root_command_executor.ts
+++ b/apps/client/src/components/root_command_executor.ts
@@ -78,15 +78,15 @@ export default class RootCommandExecutor extends Component {
}
hideLeftPaneCommand() {
- options.save(`leftPaneVisible`, "false");
+ appContext.triggerEvent("setLeftPaneVisibility", { leftPaneVisible: false });
}
showLeftPaneCommand() {
- options.save(`leftPaneVisible`, "true");
+ appContext.triggerEvent("setLeftPaneVisibility", { leftPaneVisible: true });
}
toggleLeftPaneCommand() {
- options.toggle("leftPaneVisible");
+ appContext.triggerEvent("setLeftPaneVisibility", { leftPaneVisible: null });
}
async showBackendLogCommand() {
diff --git a/apps/client/src/libraries/highlightjs/terraform.js b/apps/client/src/libraries/highlightjs/terraform.js
deleted file mode 100644
index 514e727cf..000000000
--- a/apps/client/src/libraries/highlightjs/terraform.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * highlight.js terraform syntax highlighting definition
- *
- * @see https://github.com/highlightjs/highlight.js
- *
- * :TODO:
- *
- * @package: highlightjs-terraform
- * @author: Nikos Tsirmirakis
- * @since: 2019-03-20
- *
- * Description: Terraform (HCL) language definition
- * Category: scripting
- */
-
-var module = module ? module : {}; // shim for browser use
-
-function hljsDefineTerraform(hljs) {
- var NUMBERS = {
- className: 'number',
- begin: '\\b\\d+(\\.\\d+)?',
- relevance: 0
- };
- var STRINGS = {
- className: 'string',
- begin: '"',
- end: '"',
- contains: [{
- className: 'variable',
- begin: '\\${',
- end: '\\}',
- relevance: 9,
- contains: [{
- className: 'string',
- begin: '"',
- end: '"'
- }, {
- className: 'meta',
- begin: '[A-Za-z_0-9]*' + '\\(',
- end: '\\)',
- contains: [
- NUMBERS, {
- className: 'string',
- begin: '"',
- end: '"',
- contains: [{
- className: 'variable',
- begin: '\\${',
- end: '\\}',
- contains: [{
- className: 'string',
- begin: '"',
- end: '"',
- contains: [{
- className: 'variable',
- begin: '\\${',
- end: '\\}'
- }]
- }, {
- className: 'meta',
- begin: '[A-Za-z_0-9]*' + '\\(',
- end: '\\)'
- }]
- }]
- },
- 'self']
- }]
- }]
- };
-
-return {
- aliases: ['tf', 'hcl'],
- keywords: 'resource variable provider output locals module data terraform|10',
- literal: 'false true null',
- contains: [
- hljs.COMMENT('\\#', '$'),
- NUMBERS,
- STRINGS
- ]
-}
-}
-
-hljs.registerLanguage('terraform', hljsDefineTerraform);
\ No newline at end of file
diff --git a/apps/client/src/services/content_renderer.ts b/apps/client/src/services/content_renderer.ts
index 619d2d7a6..6366e147d 100644
--- a/apps/client/src/services/content_renderer.ts
+++ b/apps/client/src/services/content_renderer.ts
@@ -12,10 +12,10 @@ import FAttachment from "../entities/fattachment.js";
import imageContextMenuService from "../menus/image_context_menu.js";
import { applySingleBlockSyntaxHighlight, applySyntaxHighlight } from "./syntax_highlight.js";
import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js";
-import { normalizeMimeTypeForCKEditor } from "./mime_type_definitions.js";
import renderDoc from "./doc_renderer.js";
import { t } from "../services/i18n.js";
import WheelZoom from 'vanilla-js-wheel-zoom';
+import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
let idCounter = 1;
diff --git a/apps/client/src/services/library_loader.ts b/apps/client/src/services/library_loader.ts
index a5ad0e3fc..431a8e2f9 100644
--- a/apps/client/src/services/library_loader.ts
+++ b/apps/client/src/services/library_loader.ts
@@ -1,7 +1,3 @@
-import mimeTypesService from "./mime_types.js";
-import optionsService from "./options.js";
-import { getStylesheetUrl } from "./syntax_highlight.js";
-
export interface Library {
js?: string[] | (() => string[]);
css?: string[];
@@ -12,32 +8,6 @@ const KATEX: Library = {
css: ["node_modules/katex/dist/katex.min.css"]
};
-const HIGHLIGHT_JS: Library = {
- js: () => {
- const mimeTypes = mimeTypesService.getMimeTypes();
- const scriptsToLoad = new Set();
- scriptsToLoad.add("node_modules/@highlightjs/cdn-assets/highlight.min.js");
- for (const mimeType of mimeTypes) {
- const id = mimeType.highlightJs;
- if (!mimeType.enabled || !id) {
- continue;
- }
-
- if (mimeType.highlightJsSource === "libraries") {
- scriptsToLoad.add(`libraries/highlightjs/${id}.js`);
- } else {
- // Built-in module.
- scriptsToLoad.add(`node_modules/@highlightjs/cdn-assets/languages/${id}.min.js`);
- }
- }
-
- const currentTheme = String(optionsService.get("codeBlockTheme"));
- loadHighlightingTheme(currentTheme);
-
- return Array.from(scriptsToLoad);
- }
-};
-
async function requireLibrary(library: Library) {
if (library.css) {
library.css.map((cssUrl) => requireCss(cssUrl));
@@ -91,36 +61,8 @@ async function requireCss(url: string, prependAssetPath = true) {
}
}
-let highlightingThemeEl: JQuery | null = null;
-function loadHighlightingTheme(theme: string) {
- if (!theme) {
- return;
- }
-
- if (theme === "none") {
- // Deactivate the theme.
- if (highlightingThemeEl) {
- highlightingThemeEl.remove();
- highlightingThemeEl = null;
- }
- return;
- }
-
- if (!highlightingThemeEl) {
- highlightingThemeEl = $(``);
- $("head").append(highlightingThemeEl);
- }
-
- const url = getStylesheetUrl(theme);
- if (url) {
- highlightingThemeEl.attr("href", url);
- }
-}
-
export default {
requireCss,
requireLibrary,
- loadHighlightingTheme,
- KATEX,
- HIGHLIGHT_JS
+ KATEX
};
diff --git a/apps/client/src/services/mime_type_definitions.ts b/apps/client/src/services/mime_type_definitions.ts
deleted file mode 100644
index 10c73e113..000000000
--- a/apps/client/src/services/mime_type_definitions.ts
+++ /dev/null
@@ -1,223 +0,0 @@
-// TODO: deduplicate with /src/services/import/mime_type_definitions.ts
-
-/**
- * A pseudo-MIME type which is used in the editor to automatically determine the language used in code blocks via heuristics.
- */
-export const MIME_TYPE_AUTO = "text-x-trilium-auto";
-
-export interface MimeTypeDefinition {
- default?: boolean;
- title: string;
- mime: string;
- /** The name of the language/mime type as defined by highlight.js (or one of the aliases), in order to be used for syntax highlighting such as inside code blocks. */
- highlightJs?: string;
- /** If specified, will load the corresponding highlight.js file from the `libraries/highlightjs/${id}.js` instead of `node_modules/@highlightjs/cdn-assets/languages/${id}.min.js`. */
- highlightJsSource?: "libraries";
- /** If specified, will load the corresponding highlight file from the given path instead of `node_modules`. */
- codeMirrorSource?: string;
-}
-
-/**
- * For highlight.js-supported languages, see https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md.
- */
-
-export const MIME_TYPES_DICT: readonly MimeTypeDefinition[] = Object.freeze([
- { title: "Plain text", mime: "text/plain", highlightJs: "plaintext", default: true },
-
- // Keep sorted alphabetically.
- { title: "APL", mime: "text/apl" },
- { title: "ASN.1", mime: "text/x-ttcn-asn" },
- { title: "ASP.NET", mime: "application/x-aspx" },
- { title: "Asterisk", mime: "text/x-asterisk" },
- { title: "Batch file (DOS)", mime: "application/x-bat", highlightJs: "dos", codeMirrorSource: "libraries/codemirror/batch.js" },
- { title: "Brainfuck", mime: "text/x-brainfuck", highlightJs: "brainfuck" },
- { title: "C", mime: "text/x-csrc", highlightJs: "c", default: true },
- { title: "C#", mime: "text/x-csharp", highlightJs: "csharp", default: true },
- { title: "C++", mime: "text/x-c++src", highlightJs: "cpp", default: true },
- { title: "Clojure", mime: "text/x-clojure", highlightJs: "clojure" },
- { title: "ClojureScript", mime: "text/x-clojurescript" },
- { title: "Closure Stylesheets (GSS)", mime: "text/x-gss" },
- { title: "CMake", mime: "text/x-cmake", highlightJs: "cmake" },
- { title: "Cobol", mime: "text/x-cobol" },
- { title: "CoffeeScript", mime: "text/coffeescript", highlightJs: "coffeescript" },
- { title: "Common Lisp", mime: "text/x-common-lisp", highlightJs: "lisp" },
- { title: "CQL", mime: "text/x-cassandra" },
- { title: "Crystal", mime: "text/x-crystal", highlightJs: "crystal" },
- { title: "CSS", mime: "text/css", highlightJs: "css", default: true },
- { title: "Cypher", mime: "application/x-cypher-query" },
- { title: "Cython", mime: "text/x-cython" },
- { title: "D", mime: "text/x-d", highlightJs: "d" },
- { title: "Dart", mime: "application/dart", highlightJs: "dart" },
- { title: "diff", mime: "text/x-diff", highlightJs: "diff" },
- { title: "Django", mime: "text/x-django", highlightJs: "django" },
- { title: "Dockerfile", mime: "text/x-dockerfile", highlightJs: "dockerfile" },
- { title: "DTD", mime: "application/xml-dtd" },
- { title: "Dylan", mime: "text/x-dylan" },
- { title: "EBNF", mime: "text/x-ebnf", highlightJs: "ebnf" },
- { title: "ECL", mime: "text/x-ecl" },
- { title: "edn", mime: "application/edn" },
- { title: "Eiffel", mime: "text/x-eiffel" },
- { title: "Elm", mime: "text/x-elm", highlightJs: "elm" },
- { title: "Embedded Javascript", mime: "application/x-ejs" },
- { title: "Embedded Ruby", mime: "application/x-erb", highlightJs: "erb" },
- { title: "Erlang", mime: "text/x-erlang", highlightJs: "erlang" },
- { title: "Esper", mime: "text/x-esper" },
- { title: "F#", mime: "text/x-fsharp", highlightJs: "fsharp" },
- { title: "Factor", mime: "text/x-factor" },
- { title: "FCL", mime: "text/x-fcl" },
- { title: "Forth", mime: "text/x-forth" },
- { title: "Fortran", mime: "text/x-fortran", highlightJs: "fortran" },
- { title: "Gas", mime: "text/x-gas" },
- { title: "GDScript (Godot)", mime: "text/x-gdscript" },
- { title: "Gherkin", mime: "text/x-feature", highlightJs: "gherkin" },
- { title: "GitHub Flavored Markdown", mime: "text/x-gfm", highlightJs: "markdown" },
- { title: "Go", mime: "text/x-go", highlightJs: "go", default: true },
- { title: "Groovy", mime: "text/x-groovy", highlightJs: "groovy", default: true },
- { title: "HAML", mime: "text/x-haml", highlightJs: "haml" },
- { title: "Haskell (Literate)", mime: "text/x-literate-haskell" },
- { title: "Haskell", mime: "text/x-haskell", highlightJs: "haskell", default: true },
- { title: "Haxe", mime: "text/x-haxe", highlightJs: "haxe" },
- { title: "HTML", mime: "text/html", highlightJs: "xml", default: true },
- { title: "HTTP", mime: "message/http", highlightJs: "http", default: true },
- { title: "HXML", mime: "text/x-hxml" },
- { title: "IDL", mime: "text/x-idl" },
- { title: "Java Server Pages", mime: "application/x-jsp", highlightJs: "java" },
- { title: "Java", mime: "text/x-java", highlightJs: "java", default: true },
- { title: "Jinja2", mime: "text/jinja2" },
- { title: "JS backend", mime: "application/javascript;env=backend", highlightJs: "javascript", default: true },
- { title: "JS frontend", mime: "application/javascript;env=frontend", highlightJs: "javascript", default: true },
- { title: "JSON-LD", mime: "application/ld+json", highlightJs: "json" },
- { title: "JSON", mime: "application/json", highlightJs: "json", default: true },
- { title: "JSX", mime: "text/jsx", highlightJs: "javascript" },
- { title: "Julia", mime: "text/x-julia", highlightJs: "julia" },
- { title: "Kotlin", mime: "text/x-kotlin", highlightJs: "kotlin", default: true },
- { title: "LaTeX", mime: "text/x-latex", highlightJs: "latex" },
- { title: "LESS", mime: "text/x-less", highlightJs: "less" },
- { title: "LiveScript", mime: "text/x-livescript", highlightJs: "livescript" },
- { title: "Lua", mime: "text/x-lua", highlightJs: "lua" },
- { title: "MariaDB SQL", mime: "text/x-mariadb", highlightJs: "sql" },
- { title: "Markdown", mime: "text/x-markdown", highlightJs: "markdown", default: true },
- { title: "Mathematica", mime: "text/x-mathematica", highlightJs: "mathematica" },
- { title: "mbox", mime: "application/mbox" },
- { title: "MIPS Assembler", mime: "text/x-asm-mips", highlightJs: "mipsasm" },
- { title: "mIRC", mime: "text/mirc" },
- { title: "Modelica", mime: "text/x-modelica" },
- { title: "MS SQL", mime: "text/x-mssql", highlightJs: "sql" },
- { title: "mscgen", mime: "text/x-mscgen" },
- { title: "msgenny", mime: "text/x-msgenny" },
- { title: "MUMPS", mime: "text/x-mumps" },
- { title: "MySQL", mime: "text/x-mysql", highlightJs: "sql" },
- { title: "Nix", mime: "text/x-nix", highlightJs: "nix" },
- { title: "Nginx", mime: "text/x-nginx-conf", highlightJs: "nginx" },
- { title: "NSIS", mime: "text/x-nsis", highlightJs: "nsis" },
- { title: "NTriples", mime: "application/n-triples" },
- { title: "Objective-C", mime: "text/x-objectivec", highlightJs: "objectivec" },
- { title: "OCaml", mime: "text/x-ocaml", highlightJs: "ocaml" },
- { title: "Octave", mime: "text/x-octave" },
- { title: "Oz", mime: "text/x-oz" },
- { title: "Pascal", mime: "text/x-pascal", highlightJs: "delphi" },
- { title: "PEG.js", mime: "null" },
- { title: "Perl", mime: "text/x-perl", default: true },
- { title: "PGP", mime: "application/pgp" },
- { title: "PHP", mime: "text/x-php", default: true, highlightJs: "php" },
- { title: "Pig", mime: "text/x-pig" },
- { title: "PLSQL", mime: "text/x-plsql", highlightJs: "sql" },
- { title: "PostgreSQL", mime: "text/x-pgsql", highlightJs: "pgsql" },
- { title: "PowerShell", mime: "application/x-powershell", highlightJs: "powershell" },
- { title: "Properties files", mime: "text/x-properties", highlightJs: "properties" },
- { title: "ProtoBuf", mime: "text/x-protobuf", highlightJs: "protobuf" },
- { title: "Pug", mime: "text/x-pug" },
- { title: "Puppet", mime: "text/x-puppet", highlightJs: "puppet" },
- { title: "Python", mime: "text/x-python", highlightJs: "python", default: true },
- { title: "Q", mime: "text/x-q", highlightJs: "q" },
- { title: "R", mime: "text/x-rsrc", highlightJs: "r" },
- { title: "reStructuredText", mime: "text/x-rst" },
- { title: "RPM Changes", mime: "text/x-rpm-changes" },
- { title: "RPM Spec", mime: "text/x-rpm-spec" },
- { title: "Ruby", mime: "text/x-ruby", highlightJs: "ruby", default: true },
- { title: "Rust", mime: "text/x-rustsrc", highlightJs: "rust" },
- { title: "SAS", mime: "text/x-sas", highlightJs: "sas" },
- { title: "Sass", mime: "text/x-sass", highlightJs: "scss" },
- { title: "Scala", mime: "text/x-scala" },
- { title: "Scheme", mime: "text/x-scheme" },
- { title: "SCSS", mime: "text/x-scss", highlightJs: "scss" },
- { title: "Shell (bash)", mime: "text/x-sh", highlightJs: "bash", default: true },
- { title: "Sieve", mime: "application/sieve" },
- { title: "Slim", mime: "text/x-slim" },
- { title: "Smalltalk", mime: "text/x-stsrc", highlightJs: "smalltalk" },
- { title: "Smarty", mime: "text/x-smarty" },
- { title: "SML", mime: "text/x-sml", highlightJs: "sml" },
- { title: "Solr", mime: "text/x-solr" },
- { title: "Soy", mime: "text/x-soy" },
- { title: "SPARQL", mime: "application/sparql-query" },
- { title: "Spreadsheet", mime: "text/x-spreadsheet" },
- { title: "SQL", mime: "text/x-sql", highlightJs: "sql", default: true },
- { title: "SQLite (Trilium)", mime: "text/x-sqlite;schema=trilium", highlightJs: "sql", default: true },
- { title: "SQLite", mime: "text/x-sqlite", highlightJs: "sql" },
- { title: "Squirrel", mime: "text/x-squirrel" },
- { title: "sTeX", mime: "text/x-stex" },
- { title: "Stylus", mime: "text/x-styl", highlightJs: "stylus" },
- { title: "Swift", mime: "text/x-swift", default: true },
- { title: "SystemVerilog", mime: "text/x-systemverilog" },
- { title: "Tcl", mime: "text/x-tcl", highlightJs: "tcl" },
- { title: "Terraform (HCL)", mime: "text/x-hcl", highlightJs: "terraform", highlightJsSource: "libraries", codeMirrorSource: "libraries/codemirror/hcl.js" },
- { title: "Textile", mime: "text/x-textile" },
- { title: "TiddlyWiki ", mime: "text/x-tiddlywiki" },
- { title: "Tiki wiki", mime: "text/tiki" },
- { title: "TOML", mime: "text/x-toml", highlightJs: "ini" },
- { title: "Tornado", mime: "text/x-tornado" },
- { title: "troff", mime: "text/troff" },
- { title: "TTCN_CFG", mime: "text/x-ttcn-cfg" },
- { title: "TTCN", mime: "text/x-ttcn" },
- { title: "Turtle", mime: "text/turtle" },
- { title: "Twig", mime: "text/x-twig", highlightJs: "twig" },
- { title: "TypeScript-JSX", mime: "text/typescript-jsx", highlightJs: "typescript" },
- { title: "TypeScript", mime: "application/typescript", highlightJs: "typescript" },
- { title: "VB.NET", mime: "text/x-vb", highlightJs: "vbnet" },
- { title: "VBScript", mime: "text/vbscript", highlightJs: "vbscript" },
- { title: "Velocity", mime: "text/velocity" },
- { title: "Verilog", mime: "text/x-verilog", highlightJs: "verilog" },
- { title: "VHDL", mime: "text/x-vhdl", highlightJs: "vhdl" },
- { title: "Vue.js Component", mime: "text/x-vue" },
- { title: "Web IDL", mime: "text/x-webidl" },
- { title: "XML", mime: "text/xml", highlightJs: "xml", default: true },
- { title: "XQuery", mime: "application/xquery", highlightJs: "xquery" },
- { title: "xu", mime: "text/x-xu" },
- { title: "Yacas", mime: "text/x-yacas" },
- { title: "YAML", mime: "text/x-yaml", highlightJs: "yaml", default: true },
- { title: "Z80", mime: "text/x-z80" }
-]);
-
-/**
- * Given a MIME type in the usual format (e.g. `text/csrc`), it returns a MIME type that can be passed down to the CKEditor
- * code plugin.
- *
- * @param mimeType The MIME type to normalize, in the usual format (e.g. `text/c-src`).
- * @returns the normalized MIME type (e.g. `text-c-src`).
- */
-export function normalizeMimeTypeForCKEditor(mimeType: string) {
- return mimeType.toLowerCase().replace(/[\W_]+/g, "-");
-}
-
-let byHighlightJsNameMappings: Record | null = null;
-
-/**
- * Given a Highlight.js language tag (e.g. `css`), it returns a corresponding {@link MimeTypeDefinition} if found.
- *
- * If there are multiple {@link MimeTypeDefinition}s for the language tag, then only the first one is retrieved. For example for `javascript`, the "JS frontend" mime type is returned.
- *
- * @param highlightJsName a language tag.
- * @returns the corresponding {@link MimeTypeDefinition} if found, or `undefined` otherwise.
- */
-export function getMimeTypeFromHighlightJs(highlightJsName: string) {
- if (!byHighlightJsNameMappings) {
- byHighlightJsNameMappings = {};
- for (const mimeType of MIME_TYPES_DICT) {
- if (mimeType.highlightJs && !byHighlightJsNameMappings[mimeType.highlightJs]) {
- byHighlightJsNameMappings[mimeType.highlightJs] = mimeType;
- }
- }
- }
-
- return byHighlightJsNameMappings[highlightJsName];
-}
diff --git a/apps/client/src/services/mime_types.ts b/apps/client/src/services/mime_types.ts
index f0f318141..3b72c531c 100644
--- a/apps/client/src/services/mime_types.ts
+++ b/apps/client/src/services/mime_types.ts
@@ -1,13 +1,6 @@
-import { MIME_TYPE_AUTO, MIME_TYPES_DICT, normalizeMimeTypeForCKEditor, type MimeTypeDefinition } from "./mime_type_definitions.js";
+import { normalizeMimeTypeForCKEditor, type MimeType, MIME_TYPE_AUTO, MIME_TYPES_DICT } from "@triliumnext/commons";
import options from "./options.js";
-interface MimeType extends MimeTypeDefinition {
- /**
- * True if this mime type was enabled by the user in the "Available MIME types in the dropdown" option in the Code Notes settings.
- */
- enabled: boolean;
-}
-
let mimeTypes: MimeType[] | null = null;
function loadMimeTypes() {
@@ -45,8 +38,8 @@ export function getHighlightJsNameForMime(mimeType: string) {
for (const mimeType of mimeTypes) {
// The mime stored by CKEditor is text-x-csrc instead of text/x-csrc so we keep this format for faster lookup.
const normalizedMime = normalizeMimeTypeForCKEditor(mimeType.mime);
- if (mimeType.highlightJs) {
- mimeToHighlightJsMapping[normalizedMime] = mimeType.highlightJs;
+ if (mimeType.mdLanguageCode) {
+ mimeToHighlightJsMapping[normalizedMime] = mimeType.mdLanguageCode;
}
}
}
diff --git a/apps/client/src/services/resizer.ts b/apps/client/src/services/resizer.ts
index 1cce0f993..e0dc40995 100644
--- a/apps/client/src/services/resizer.ts
+++ b/apps/client/src/services/resizer.ts
@@ -3,7 +3,11 @@ import Split from "split.js"
export const DEFAULT_GUTTER_SIZE = 5;
+let leftPaneWidth: number;
+let reservedPx: number;
+let layoutOrientation: string;
let leftInstance: ReturnType | null;
+let rightPaneWidth: number;
let rightInstance: ReturnType | null;
function setupLeftPaneResizer(leftPaneVisible: boolean) {
@@ -14,27 +18,34 @@ function setupLeftPaneResizer(leftPaneVisible: boolean) {
$("#left-pane").toggle(leftPaneVisible);
+ layoutOrientation = layoutOrientation ?? options.get("layoutOrientation");
+ reservedPx = reservedPx ?? (layoutOrientation === "vertical" ? ($("#launcher-pane").outerWidth() || 0) : 0);
+ // Window resizing causes `window.innerWidth` to change, so `reservedWidth` needs to be recalculated each time.
+ const reservedWidth = reservedPx / window.innerWidth * 100;
if (!leftPaneVisible) {
- $("#rest-pane").css("width", "100%");
-
+ $("#rest-pane").css("width", layoutOrientation === "vertical" ? `${100 - reservedWidth}%` : "100%");
return;
}
- let leftPaneWidth = options.getInt("leftPaneWidth");
+ leftPaneWidth = leftPaneWidth ?? (options.getInt("leftPaneWidth") ?? 0);
if (!leftPaneWidth || leftPaneWidth < 5) {
leftPaneWidth = 5;
}
+ const restPaneWidth = 100 - leftPaneWidth - reservedWidth;
if (leftPaneVisible) {
// Delayed initialization ensures that all DOM elements are fully rendered and part of the layout,
// preventing Split.js from retrieving incorrect dimensions due to #left-pane not being rendered yet,
// which would cause the minSize setting to have no effect.
requestAnimationFrame(() => {
leftInstance = Split(["#left-pane", "#rest-pane"], {
- sizes: [leftPaneWidth, 100 - leftPaneWidth],
+ sizes: [leftPaneWidth, restPaneWidth],
gutterSize: DEFAULT_GUTTER_SIZE,
minSize: [150, 300],
- onDragEnd: (sizes) => options.save("leftPaneWidth", Math.round(sizes[0]))
+ onDragEnd: (sizes) => {
+ leftPaneWidth = Math.round(sizes[0]);
+ options.save("leftPaneWidth", Math.round(sizes[0]));
+ }
});
});
}
@@ -54,7 +65,7 @@ function setupRightPaneResizer() {
return;
}
- let rightPaneWidth = options.getInt("rightPaneWidth");
+ rightPaneWidth = rightPaneWidth ?? (options.getInt("rightPaneWidth") ?? 0);
if (!rightPaneWidth || rightPaneWidth < 5) {
rightPaneWidth = 5;
}
@@ -63,8 +74,11 @@ function setupRightPaneResizer() {
rightInstance = Split(["#center-pane", "#right-pane"], {
sizes: [100 - rightPaneWidth, rightPaneWidth],
gutterSize: DEFAULT_GUTTER_SIZE,
- minSize: [ 300, 180 ],
- onDragEnd: (sizes) => options.save("rightPaneWidth", Math.round(sizes[1]))
+ minSize: [300, 180],
+ onDragEnd: (sizes) => {
+ rightPaneWidth = Math.round(sizes[1]);
+ options.save("rightPaneWidth", Math.round(sizes[1]));
+ }
});
}
}
diff --git a/apps/client/src/services/syntax_highlight.ts b/apps/client/src/services/syntax_highlight.ts
index ae2fbde72..7dfb29f30 100644
--- a/apps/client/src/services/syntax_highlight.ts
+++ b/apps/client/src/services/syntax_highlight.ts
@@ -1,19 +1,8 @@
-import library_loader from "./library_loader.js";
+import { ensureMimeTypes, highlight, highlightAuto, loadTheme, Themes } from "@triliumnext/highlightjs";
import mime_types from "./mime_types.js";
import options from "./options.js";
-export function getStylesheetUrl(theme: string) {
- if (!theme) {
- return null;
- }
-
- const defaultPrefix = "default:";
- if (theme.startsWith(defaultPrefix)) {
- return `${window.glob.assetPath}/node_modules/@highlightjs/cdn-assets/styles/${theme.substr(defaultPrefix.length)}.min.css`;
- }
-
- return null;
-}
+let highlightingLoaded = false;
/**
* Identifies all the code blocks (as `pre code`) under the specified hierarchy and uses the highlight.js library to obtain the highlighted text which is then applied on to the code blocks.
@@ -25,6 +14,8 @@ export async function applySyntaxHighlight($container: JQuery) {
return;
}
+ await ensureMimeTypesForHighlighting();
+
const codeBlocks = $container.find("pre code");
for (const codeBlock of codeBlocks) {
const normalizedMimeType = extractLanguageFromClassList(codeBlock);
@@ -43,20 +34,13 @@ export async function applySingleBlockSyntaxHighlight($codeBlock: JQuery .horizontal {
--launcher-pane-button-gap: var(--launcher-pane-vert-button-gap);
width: var(--launcher-pane-size) !important;
+ min-width: var(--launcher-pane-size) !important;
padding-bottom: var(--launcher-pane-button-gap);
}
diff --git a/apps/client/src/translations/cn/translation.json b/apps/client/src/translations/cn/translation.json
index bcc0bc269..455723f0e 100644
--- a/apps/client/src/translations/cn/translation.json
+++ b/apps/client/src/translations/cn/translation.json
@@ -1621,7 +1621,10 @@
"color-scheme": "颜色方案"
},
"code_block": {
- "word_wrapping": "自动换行"
+ "word_wrapping": "自动换行",
+ "theme_none": "无语法高亮",
+ "theme_group_light": "浅色主题",
+ "theme_group_dark": "深色主题"
},
"classic_editor_toolbar": {
"title": "格式化"
diff --git a/apps/client/src/translations/de/translation.json b/apps/client/src/translations/de/translation.json
index 8ec5097aa..ea17f0575 100644
--- a/apps/client/src/translations/de/translation.json
+++ b/apps/client/src/translations/de/translation.json
@@ -1573,7 +1573,10 @@
"color-scheme": "Farbschema"
},
"code_block": {
- "word_wrapping": "Wortumbruch"
+ "word_wrapping": "Wortumbruch",
+ "theme_none": "Keine Syntax-Hervorhebung",
+ "theme_group_light": "Helle Themen",
+ "theme_group_dark": "Dunkle Themen"
},
"classic_editor_toolbar": {
"title": "Format"
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index efa2e5a83..9e2cc067e 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -1827,7 +1827,10 @@
"color-scheme": "Color Scheme"
},
"code_block": {
- "word_wrapping": "Word wrapping"
+ "word_wrapping": "Word wrapping",
+ "theme_none": "No syntax highlighting",
+ "theme_group_light": "Light themes",
+ "theme_group_dark": "Dark themes"
},
"classic_editor_toolbar": {
"title": "Formatting"
diff --git a/apps/client/src/translations/es/translation.json b/apps/client/src/translations/es/translation.json
index 46be1d55f..64951c3a6 100644
--- a/apps/client/src/translations/es/translation.json
+++ b/apps/client/src/translations/es/translation.json
@@ -1589,7 +1589,10 @@
"color-scheme": "Esquema de color"
},
"code_block": {
- "word_wrapping": "Ajuste de palabras"
+ "word_wrapping": "Ajuste de palabras",
+ "theme_none": "Sin resaltado de sintaxis",
+ "theme_group_light": "Temas claros",
+ "theme_group_dark": "Temas oscuros"
},
"classic_editor_toolbar": {
"title": "Formato"
diff --git a/apps/client/src/translations/fr/translation.json b/apps/client/src/translations/fr/translation.json
index d7597a3ac..74335e936 100644
--- a/apps/client/src/translations/fr/translation.json
+++ b/apps/client/src/translations/fr/translation.json
@@ -1579,7 +1579,10 @@
"color-scheme": "Jeu de couleurs"
},
"code_block": {
- "word_wrapping": "Saut à la ligne automatique suivant la largeur"
+ "word_wrapping": "Saut à la ligne automatique suivant la largeur",
+ "theme_none": "Pas de coloration syntaxique",
+ "theme_group_light": "Thèmes clairs",
+ "theme_group_dark": "Thèmes sombres"
},
"classic_editor_toolbar": {
"title": "Mise en forme"
diff --git a/apps/client/src/translations/pt_br/translation.json b/apps/client/src/translations/pt_br/translation.json
index a79d14844..8410e64db 100644
--- a/apps/client/src/translations/pt_br/translation.json
+++ b/apps/client/src/translations/pt_br/translation.json
@@ -1,5 +1,10 @@
{
"revisions": {
"delete_button": ""
+ },
+ "code_block": {
+ "theme_none": "Sem destaque de sintaxe",
+ "theme_group_light": "Temas claros",
+ "theme_group_dark": "Temas escuros"
}
}
diff --git a/apps/client/src/translations/ro/translation.json b/apps/client/src/translations/ro/translation.json
index efaf270f2..25b073e9e 100644
--- a/apps/client/src/translations/ro/translation.json
+++ b/apps/client/src/translations/ro/translation.json
@@ -1585,7 +1585,10 @@
"description": "Controlează evidențierea de sintaxă pentru blocurile de cod în interiorul notițelor text, notițele de tip cod nu vor fi afectate de aceste setări."
},
"code_block": {
- "word_wrapping": "Încadrare text"
+ "word_wrapping": "Încadrare text",
+ "theme_none": "Fără evidențiere de sintaxă",
+ "theme_group_dark": "Teme întunecate",
+ "theme_group_light": "Teme luminoase"
},
"classic_editor_toolbar": {
"title": "Formatare"
diff --git a/apps/client/src/translations/tw/translation.json b/apps/client/src/translations/tw/translation.json
index 4360fcdf4..c25504510 100644
--- a/apps/client/src/translations/tw/translation.json
+++ b/apps/client/src/translations/tw/translation.json
@@ -1519,7 +1519,10 @@
"color-scheme": "顏色方案"
},
"code_block": {
- "word_wrapping": "自動換行"
+ "word_wrapping": "自動換行",
+ "theme_none": "無格式高亮",
+ "theme_group_light": "淺色主題",
+ "theme_group_dark": "深色主題"
},
"classic_editor_toolbar": {
"title": "格式化"
diff --git a/apps/client/src/types.d.ts b/apps/client/src/types.d.ts
index 70e51aede..fad0fed16 100644
--- a/apps/client/src/types.d.ts
+++ b/apps/client/src/types.d.ts
@@ -124,13 +124,6 @@ declare global {
var __non_webpack_require__: RequireMethod | undefined;
// Libraries
- // TODO: Replace once library loader is replaced with webpack.
- var hljs: {
- highlightAuto(text: string);
- highlight(text: string, {
- language: string
- });
- };
var renderMathInElement: (element: HTMLElement, options: {
trust: boolean;
}) => void;
diff --git a/apps/client/src/widgets/buttons/global_menu.ts b/apps/client/src/widgets/buttons/global_menu.ts
index d21e43b0d..47323e271 100644
--- a/apps/client/src/widgets/buttons/global_menu.ts
+++ b/apps/client/src/widgets/buttons/global_menu.ts
@@ -53,10 +53,6 @@ const TPL = /*html*/`
pointer-events: none;
}
- .update-to-latest-version-button {
- display: none;
- }
-
.global-menu .zoom-container {
display: flex;
flex-direction: row;
@@ -235,7 +231,7 @@ const TPL = /*html*/`
${t("global_menu.about")}
-
+
diff --git a/apps/client/src/widgets/buttons/left_pane_toggle.ts b/apps/client/src/widgets/buttons/left_pane_toggle.ts
index 88528add5..22a902622 100644
--- a/apps/client/src/widgets/buttons/left_pane_toggle.ts
+++ b/apps/client/src/widgets/buttons/left_pane_toggle.ts
@@ -19,10 +19,10 @@ export default class LeftPaneToggleWidget extends CommandButtonWidget {
return "bx-sidebar";
}
- return options.is("leftPaneVisible") ? "bx-chevrons-left" : "bx-chevrons-right";
+ return this.currentLeftPaneVisible ? "bx-chevrons-left" : "bx-chevrons-right";
};
- this.settings.title = () => (options.is("leftPaneVisible") ? t("left_pane_toggle.hide_panel") : t("left_pane_toggle.show_panel"));
+ this.settings.title = () => (this.currentLeftPaneVisible ? t("left_pane_toggle.hide_panel") : t("left_pane_toggle.show_panel"));
this.settings.command = () => (this.currentLeftPaneVisible ? "hideLeftPane" : "showLeftPane");
@@ -32,16 +32,12 @@ export default class LeftPaneToggleWidget extends CommandButtonWidget {
}
refreshIcon() {
- if (document.hasFocus() || this.currentLeftPaneVisible === true) {
- super.refreshIcon();
- splitService.setupLeftPaneResizer(this.currentLeftPaneVisible);
- }
+ super.refreshIcon();
+ splitService.setupLeftPaneResizer(this.currentLeftPaneVisible);
}
-
- entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
- if (loadResults.isOptionReloaded("leftPaneVisible") && document.hasFocus()) {
- this.currentLeftPaneVisible = options.is("leftPaneVisible");
- this.refreshIcon();
- }
+
+ setLeftPaneVisibilityEvent({ leftPaneVisible }: EventData<"setLeftPaneVisibility">) {
+ this.currentLeftPaneVisible = leftPaneVisible ?? !this.currentLeftPaneVisible;
+ this.refreshIcon();
}
}
diff --git a/apps/client/src/widgets/containers/left_pane_container.ts b/apps/client/src/widgets/containers/left_pane_container.ts
index 2b8221553..d95f5091a 100644
--- a/apps/client/src/widgets/containers/left_pane_container.ts
+++ b/apps/client/src/widgets/containers/left_pane_container.ts
@@ -4,28 +4,33 @@ import appContext, { type EventData } from "../../components/app_context.js";
import type Component from "../../components/component.js";
export default class LeftPaneContainer extends FlexContainer {
+ private currentLeftPaneVisible: boolean;
+
constructor() {
super("column");
+ this.currentLeftPaneVisible = options.is("leftPaneVisible");
+
this.id("left-pane");
this.css("height", "100%");
this.collapsible();
}
isEnabled() {
- return super.isEnabled() && options.is("leftPaneVisible");
+ return super.isEnabled() && this.currentLeftPaneVisible;
}
- entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
- if (loadResults.isOptionReloaded("leftPaneVisible") && document.hasFocus()) {
- const visible = this.isEnabled();
- this.toggleInt(visible);
+ setLeftPaneVisibilityEvent({ leftPaneVisible }: EventData<"setLeftPaneVisibility">) {
+ this.currentLeftPaneVisible = leftPaneVisible ?? !this.currentLeftPaneVisible;
+ const visible = this.isEnabled();
+ this.toggleInt(visible);
- if (visible) {
- this.triggerEvent("focusTree", {});
- } else {
- this.triggerEvent("focusOnDetail", { ntxId: appContext.tabManager.getActiveContext()?.ntxId });
- }
+ if (visible) {
+ this.triggerEvent("focusTree", {});
+ } else {
+ this.triggerEvent("focusOnDetail", { ntxId: appContext.tabManager.getActiveContext()?.ntxId });
}
+
+ options.save("leftPaneVisible", this.currentLeftPaneVisible.toString());
}
}
diff --git a/apps/client/src/widgets/note_detail.ts b/apps/client/src/widgets/note_detail.ts
index 238683809..3b232134a 100644
--- a/apps/client/src/widgets/note_detail.ts
+++ b/apps/client/src/widgets/note_detail.ts
@@ -3,12 +3,10 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js";
import protectedSessionHolder from "../services/protected_session_holder.js";
import SpacedUpdate from "../services/spaced_update.js";
import server from "../services/server.js";
-import libraryLoader from "../services/library_loader.js";
import appContext, { type CommandListenerData, type EventData } from "../components/app_context.js";
import keyboardActionsService from "../services/keyboard_actions.js";
import noteCreateService from "../services/note_create.js";
import attributeService from "../services/attributes.js";
-import attributeRenderer from "../services/attribute_renderer.js";
import EmptyTypeWidget from "./type_widgets/empty.js";
import EditableTextTypeWidget from "./type_widgets/editable_text.js";
@@ -30,7 +28,6 @@ import ContentWidgetTypeWidget from "./type_widgets/content_widget.js";
import AttachmentListTypeWidget from "./type_widgets/attachment_list.js";
import AttachmentDetailTypeWidget from "./type_widgets/attachment_detail.js";
import MindMapWidget from "./type_widgets/mind_map.js";
-import { getStylesheetUrl, isSyntaxHighlightEnabled } from "../services/syntax_highlight.js";
import GeoMapTypeWidget from "./type_widgets/geo_map.js";
import utils from "../services/utils.js";
import type { NoteType } from "../entities/fnote.js";
diff --git a/apps/client/src/widgets/type_widgets/ckeditor/config.ts b/apps/client/src/widgets/type_widgets/ckeditor/config.ts
index b4ba9633a..e58fac8f1 100644
--- a/apps/client/src/widgets/type_widgets/ckeditor/config.ts
+++ b/apps/client/src/widgets/type_widgets/ckeditor/config.ts
@@ -1,9 +1,8 @@
-import library_loader from "../../../services/library_loader.js";
import { ALLOWED_PROTOCOLS } from "../../../services/link.js";
-import { MIME_TYPE_AUTO } from "../../../services/mime_type_definitions.js";
+import { MIME_TYPE_AUTO } from "@triliumnext/commons";
import { getHighlightJsNameForMime } from "../../../services/mime_types.js";
import options from "../../../services/options.js";
-import { isSyntaxHighlightEnabled } from "../../../services/syntax_highlight.js";
+import { ensureMimeTypesForHighlighting, isSyntaxHighlightEnabled } from "../../../services/syntax_highlight.js";
import utils from "../../../services/utils.js";
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?external";
@@ -104,9 +103,9 @@ export function buildConfig() {
definitionsUrl: emojiDefinitionsUrl
},
syntaxHighlighting: {
- async loadHighlightJs() {
- await library_loader.requireLibrary(library_loader.HIGHLIGHT_JS);
- return hljs;
+ loadHighlightJs: async () => {
+ await ensureMimeTypesForHighlighting();
+ return await import("@triliumnext/highlightjs");
},
mapLanguageName: getHighlightJsNameForMime,
defaultMimeType: MIME_TYPE_AUTO,
diff --git a/apps/client/src/widgets/type_widgets/editable_text.ts b/apps/client/src/widgets/type_widgets/editable_text.ts
index 1670a0ce1..b67eed26a 100644
--- a/apps/client/src/widgets/type_widgets/editable_text.ts
+++ b/apps/client/src/widgets/type_widgets/editable_text.ts
@@ -12,13 +12,13 @@ import appContext, { type CommandListenerData, type EventData } from "../../comp
import dialogService from "../../services/dialog.js";
import options from "../../services/options.js";
import toast from "../../services/toast.js";
-import { normalizeMimeTypeForCKEditor } from "../../services/mime_type_definitions.js";
import { buildSelectedBackgroundColor } from "../../components/touch_bar.js";
import { buildConfig, buildToolbarConfig } from "./ckeditor/config.js";
import type FNote from "../../entities/fnote.js";
import { getMermaidConfig } from "../../services/mermaid.js";
import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig } from "@triliumnext/ckeditor5";
import "@triliumnext/ckeditor5/index.css";
+import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
const ENABLE_INSPECTOR = false;
diff --git a/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts b/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts
index 162499274..0a97d37d3 100644
--- a/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts
+++ b/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts
@@ -1,10 +1,11 @@
-import type { OptionMap } from "@triliumnext/commons";
+import { normalizeMimeTypeForCKEditor, type OptionMap } from "@triliumnext/commons";
import { t } from "../../../../services/i18n.js";
-import library_loader from "../../../../services/library_loader.js";
import server from "../../../../services/server.js";
import OptionsWidget from "../options_widget.js";
+import { ensureMimeTypesForHighlighting, loadHighlightingTheme } from "../../../../services/syntax_highlight.js";
+import { Themes, type Theme } from "@triliumnext/highlightjs";
-const SAMPLE_LANGUAGE = "javascript";
+const SAMPLE_LANGUAGE = normalizeMimeTypeForCKEditor("application/javascript;env=frontend");
const SAMPLE_CODE = `\
const n = 10;
greet(n); // Print "Hello World" for n times
@@ -55,14 +56,6 @@ const TPL = /*html*/`
`;
-// TODO: Deduplicate
-interface Theme {
- title: string;
- val: string;
-}
-
-type Response = Record;
-
/**
* Contains appearance settings for code blocks within text notes, such as the theme for the syntax highlighter.
*/
@@ -75,9 +68,31 @@ export default class CodeBlockOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$themeSelect = this.$widget.find(".theme-select");
+
+ // Populate the list of themes.
+ const themeGroups = groupThemesByLightOrDark();
+ for (const [key, themes] of Object.entries(themeGroups)) {
+ const $group = key ? $("