Merge branch 'develop' into feature/custom-datetime-format

This commit is contained in:
vanndoublen 2025-05-20 19:55:45 +08:00 committed by GitHub
commit f640c9212e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
78 changed files with 2672 additions and 1783 deletions

104
README.md
View File

@ -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:
<a href="https://triliumnext.github.io/Docs/Wiki/screenshot-tour"><img src="./docs/app.png" alt="Trilium Screenshot" width="1000"></a>
## 🎁 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.
[![Packaging status](https://repology.org/badge/vertical-allrepos/trilium-next-desktop.svg)](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.

View File

@ -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",

View File

@ -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",

View File

@ -283,6 +283,9 @@ export type CommandMappings = {
type EventMappings = {
initialRenderComplete: {};
frocaReloaded: {};
setLeftPaneVisibility: {
leftPaneVisible: boolean | null;
}
protectedSessionStarted: {};
notesReloaded: {
noteIds: string[];

View File

@ -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() {

View File

@ -1,83 +0,0 @@
/*
* highlight.js terraform syntax highlighting definition
*
* @see https://github.com/highlightjs/highlight.js
*
* :TODO:
*
* @package: highlightjs-terraform
* @author: Nikos Tsirmirakis <nikos.tsirmirakis@winopsdba.com>
* @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);

View File

@ -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;

View File

@ -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<string>();
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<HTMLElement> | 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 = $(`<link rel="stylesheet" type="text/css" />`);
$("head").append(highlightingThemeEl);
}
const url = getStylesheetUrl(theme);
if (url) {
highlightingThemeEl.attr("href", url);
}
}
export default {
requireCss,
requireLibrary,
loadHighlightingTheme,
KATEX,
HIGHLIGHT_JS
KATEX
};

View File

@ -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<string, MimeTypeDefinition> | 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];
}

View File

@ -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;
}
}
}

View File

@ -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<typeof Split> | null;
let rightPaneWidth: number;
let rightInstance: ReturnType<typeof Split> | 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]));
}
});
}
}

View File

@ -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<HTMLElement>) {
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<HTMLEle
$codeBlock.parent().toggleClass("hljs");
const text = $codeBlock.text();
if (!window.hljs) {
await library_loader.requireLibrary(library_loader.HIGHLIGHT_JS);
}
let highlightedText = null;
if (normalizedMimeType === mime_types.MIME_TYPE_AUTO) {
highlightedText = hljs.highlightAuto(text);
await ensureMimeTypesForHighlighting();
highlightedText = highlightAuto(text);
} else if (normalizedMimeType) {
const language = mime_types.getHighlightJsNameForMime(normalizedMimeType);
if (language) {
highlightedText = hljs.highlight(text, { language });
} else {
console.warn(`Unknown mime type: ${normalizedMimeType}.`);
}
await ensureMimeTypesForHighlighting();
highlightedText = highlight(text, { language: normalizedMimeType });
}
if (highlightedText) {
@ -64,6 +48,35 @@ export async function applySingleBlockSyntaxHighlight($codeBlock: JQuery<HTMLEle
}
}
export async function ensureMimeTypesForHighlighting() {
if (highlightingLoaded) {
return;
}
// Load theme.
const currentThemeName = String(options.get("codeBlockTheme"));
loadHighlightingTheme(currentThemeName);
// Load mime types.
const mimeTypes = mime_types.getMimeTypes();
await ensureMimeTypes(mimeTypes);
highlightingLoaded = true;
}
export function loadHighlightingTheme(themeName: string) {
const themePrefix = "default:";
let theme = null;
if (themeName.includes(themePrefix)) {
theme = Themes[themeName.substring(themePrefix.length)];
}
if (!theme) {
theme = Themes.default;
}
loadTheme(theme);
}
/**
* Indicates whether syntax highlighting should be enabled for code blocks, by querying the value of the `codeblockTheme` option.
* @returns whether syntax highlighting should be enabled for code blocks.

View File

@ -127,6 +127,7 @@ body.layout-horizontal > .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);
}

View File

@ -1621,7 +1621,10 @@
"color-scheme": "颜色方案"
},
"code_block": {
"word_wrapping": "自动换行"
"word_wrapping": "自动换行",
"theme_none": "无语法高亮",
"theme_group_light": "浅色主题",
"theme_group_dark": "深色主题"
},
"classic_editor_toolbar": {
"title": "格式化"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"
}
}

View File

@ -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"

View File

@ -1519,7 +1519,10 @@
"color-scheme": "顏色方案"
},
"code_block": {
"word_wrapping": "自動換行"
"word_wrapping": "自動換行",
"theme_none": "無格式高亮",
"theme_group_light": "淺色主題",
"theme_group_dark": "深色主題"
},
"classic_editor_toolbar": {
"title": "格式化"

View File

@ -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;

View File

@ -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")}
</li>
<li class="dropdown-item update-to-latest-version-button" data-trigger-command="downloadLatestVersion">
<li class="dropdown-item update-to-latest-version-button" style="display: none;" data-trigger-command="downloadLatestVersion">
<span class="bx bx-sync"></span>
<span class="version-text"></span>

View File

@ -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();
}
}

View File

@ -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<Component> {
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());
}
}

View File

@ -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";

View File

@ -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,

View File

@ -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;

View File

@ -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*/`
</div>
`;
// TODO: Deduplicate
interface Theme {
title: string;
val: string;
}
type Response = Record<string, Theme[]>;
/**
* 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 ? $("<optgroup>").attr("label", key) : null;
for (const theme of themes) {
const option = $("<option>")
.attr("value", theme.val)
.text(theme.title);
if ($group) {
$group.append(option);
} else {
this.$themeSelect.append(option);
}
}
if ($group) {
this.$themeSelect.append($group);
}
}
this.$themeSelect.on("change", async () => {
const newTheme = String(this.$themeSelect.val());
library_loader.loadHighlightingTheme(newTheme);
loadHighlightingTheme(newTheme);
await server.put(`options/codeBlockTheme/${newTheme}`);
});
@ -91,11 +106,14 @@ export default class CodeBlockOptions extends OptionsWidget {
#setupPreview(shouldEnableSyntaxHighlight: boolean) {
const text = SAMPLE_CODE;
if (shouldEnableSyntaxHighlight) {
library_loader.requireLibrary(library_loader.HIGHLIGHT_JS).then(() => {
import("@triliumnext/highlightjs").then(async (hljs) => {
await ensureMimeTypesForHighlighting();
const highlightedText = hljs.highlight(text, {
language: SAMPLE_LANGUAGE
});
this.$sampleEl.html(highlightedText.value);
if (highlightedText) {
this.$sampleEl.html(highlightedText.value);
}
});
} else {
this.$sampleEl.text(text);
@ -103,25 +121,6 @@ export default class CodeBlockOptions extends OptionsWidget {
}
async optionsLoaded(options: OptionMap) {
const themeGroups = await server.get<Response>("options/codeblock-themes");
this.$themeSelect.empty();
for (const [key, themes] of Object.entries(themeGroups)) {
const $group = key ? $("<optgroup>").attr("label", key) : null;
for (const theme of themes) {
const option = $("<option>").attr("value", theme.val).text(theme.title);
if ($group) {
$group.append(option);
} else {
this.$themeSelect.append(option);
}
}
if ($group) {
this.$themeSelect.append($group);
}
}
this.$themeSelect.val(options.codeBlockTheme);
this.setCheckboxState(this.$wordWrap, options.codeBlockWordWrap);
this.$widget.closest(".note-detail-printable").toggleClass("word-wrap", options.codeBlockWordWrap === "true");
@ -129,3 +128,38 @@ export default class CodeBlockOptions extends OptionsWidget {
this.#setupPreview(options.codeBlockTheme !== "none");
}
}
interface ThemeData {
val: string;
title: string;
}
function groupThemesByLightOrDark() {
const darkThemes: ThemeData[] = [];
const lightThemes: ThemeData[] = [];
for (const [ id, theme ] of Object.entries(Themes)) {
const data: ThemeData = {
val: "default:" + id,
title: theme.name
};
if (theme.name.includes("Dark")) {
darkThemes.push(data);
} else {
lightThemes.push(data);
}
}
const output: Record<string, ThemeData[]> = {
"": [
{
val: "none",
title: t("code_block.theme_none")
}
]
};
output[t("code_block.theme_group_light")] = lightThemes;
output[t("code_block.theme_group_dark")] = darkThemes;
return output;
}

View File

@ -34,6 +34,9 @@
"src/**/*.ts"
],
"references": [
{
"path": "../../packages/highlightjs/tsconfig.lib.json"
},
{
"path": "../../packages/codemirror/tsconfig.lib.json"
},

View File

@ -3,6 +3,9 @@
"files": [],
"include": [],
"references": [
{
"path": "../../packages/highlightjs"
},
{
"path": "../../packages/codemirror"
},

View File

@ -80,6 +80,7 @@ module.exports = composePlugins(
}))
}));
inlineCss(config);
inlineSvg(config);
externalJson(config);
@ -103,6 +104,17 @@ function inlineSvg(config) {
});
}
function inlineCss(config) {
if (!config.module?.rules) {
return;
}
// Alter Nx's asset rule to avoid inlining SVG if they have ?raw prepended.
console.log(config.module.rules.map((r) => r.test.toString()));
const existingRule = config.module.rules.find((r) => r.test.toString().startsWith("/\\.css"));
existingRule.resourceQuery = { not: [/raw/] };
}
function externalJson(config) {
if (!config.module?.rules) {
return;

View File

@ -67,8 +67,5 @@
}
}
}
},
"devDependencies": {
"@types/diff": "^7.0.2"
}
}

View File

@ -11,8 +11,7 @@
"electron-dl": "4.0.0",
"electron-squirrel-startup": "1.0.1",
"jquery.fancytree": "2.38.5",
"jquery-hotkeys": "0.2.2",
"@highlightjs/cdn-assets": "11.11.1"
"jquery-hotkeys": "0.2.2"
},
"devDependencies": {
"@types/electron-squirrel-startup": "1.0.2",

View File

@ -6,8 +6,7 @@
"dependencies": {
"better-sqlite3": "11.10.0",
"jquery.fancytree": "2.38.5",
"jquery-hotkeys": "0.2.2",
"@highlightjs/cdn-assets": "11.11.1"
"jquery-hotkeys": "0.2.2"
},
"devDependencies": {
"@electron/remote": "2.1.2",
@ -83,7 +82,7 @@
"html2plaintext": "2.1.4",
"http-proxy-agent": "7.0.2",
"https-proxy-agent": "7.0.6",
"i18next": "25.1.3",
"i18next": "25.2.0",
"i18next-fs-backend": "2.6.0",
"image-type": "5.2.0",
"ini": "5.0.0",
@ -92,9 +91,9 @@
"jimp": "1.6.0",
"js-yaml": "4.1.0",
"jsdom": "26.1.0",
"marked": "15.0.11",
"marked": "15.0.12",
"mime-types": "3.0.1",
"multer": "1.4.5-lts.2",
"multer": "2.0.0",
"normalize-strings": "1.1.1",
"ollama": "0.5.15",
"openai": "4.100.0",

View File

@ -193,11 +193,6 @@
"special_notes": {
"search_prefix": "搜索:"
},
"code_block": {
"theme_none": "无语法高亮",
"theme_group_light": "浅色主题",
"theme_group_dark": "深色主题"
},
"test_sync": {
"not-configured": "同步服务器主机未配置。请先配置同步。",
"successful": "同步服务器握手成功,同步已开始。"

View File

@ -185,11 +185,6 @@
"special_notes": {
"search_prefix": "Suche:"
},
"code_block": {
"theme_none": "Keine Syntax-Hervorhebung",
"theme_group_light": "Helle Themen",
"theme_group_dark": "Dunkle Themen"
},
"test_sync": {
"not-configured": "Der Synchronisations-Server-Host ist nicht konfiguriert. Bitte konfiguriere zuerst die Synchronisation.",
"successful": "Die Server-Verbindung wurde erfolgreich hergestellt, die Synchronisation wurde gestartet."

View File

@ -193,11 +193,6 @@
"special_notes": {
"search_prefix": "Search:"
},
"code_block": {
"theme_none": "No syntax highlighting",
"theme_group_light": "Light themes",
"theme_group_dark": "Dark themes"
},
"test_sync": {
"not-configured": "Sync server host is not configured. Please configure sync first.",
"successful": "Sync server handshake has been successful, sync has been started."

View File

@ -189,11 +189,6 @@
"special_notes": {
"search_prefix": "Buscar:"
},
"code_block": {
"theme_none": "Sin resaltado de sintaxis",
"theme_group_light": "Temas claros",
"theme_group_dark": "Temas oscuros"
},
"test_sync": {
"not-configured": "El servidor de sincronización no está configurado. Por favor configure primero la sincronización.",
"successful": "El protocolo de enlace del servidor de sincronización ha sido exitoso, la sincronización ha comenzado."

View File

@ -189,11 +189,6 @@
"special_notes": {
"search_prefix": "Recherche :"
},
"code_block": {
"theme_none": "Pas de coloration syntaxique",
"theme_group_light": "Thèmes clairs",
"theme_group_dark": "Thèmes sombres"
},
"test_sync": {
"not-configured": "L'hôte du serveur de synchronisation n'est pas configuré. Veuillez d'abord configurer la synchronisation.",
"successful": "L'établissement de liaison du serveur de synchronisation a été réussi, la synchronisation a été démarrée."

View File

@ -186,11 +186,6 @@
"special_notes": {
"search_prefix": "Pesquisar:"
},
"code_block": {
"theme_none": "Sem destaque de sintaxe",
"theme_group_light": "Temas claros",
"theme_group_dark": "Temas escuros"
},
"test_sync": {
"not-configured": "O host do servidor de sincronização não está configurado. Por favor, configure a sincronização primeiro.",
"successful": "A comunicação com o servidor de sincronização foi bem-sucedida, a sincronização foi iniciada."

View File

@ -189,11 +189,6 @@
"special_notes": {
"search_prefix": "Căutare:"
},
"code_block": {
"theme_none": "Fără evidențiere de sintaxă",
"theme_group_dark": "Teme întunecate",
"theme_group_light": "Teme luminoase"
},
"test_sync": {
"not-configured": "Calea către serverul de sincronizare nu este configurată. Configurați sincronizarea înainte.",
"successful": "Comunicarea cu serverul de sincronizare a avut loc cu succes, s-a început sincronizarea."

View File

@ -185,11 +185,6 @@
"special_notes": {
"search_prefix": "搜尋:"
},
"code_block": {
"theme_none": "無格式高亮",
"theme_group_light": "淺色主題",
"theme_group_dark": "深色主題"
},
"test_sync": {
"not-configured": "並未設定同步伺服器主機,請先設定同步",
"successful": "成功與同步伺服器握手,現在開始同步"

View File

@ -6,7 +6,6 @@ import searchService from "../../services/search/services/search.js";
import ValidationError from "../../errors/validation_error.js";
import type { Request } from "express";
import { changeLanguage, getLocales } from "../../services/i18n.js";
import { listSyntaxHighlightingThemes } from "../../services/code_block_theme.js";
import type { OptionNames } from "@triliumnext/commons";
// options allowed to be updated directly in the Options dialog
@ -191,10 +190,6 @@ function getUserThemes() {
return ret;
}
function getSyntaxHighlightingThemes() {
return listSyntaxHighlightingThemes();
}
function getSupportedLocales() {
return getLocales();
}
@ -211,6 +206,5 @@ export default {
updateOption,
updateOptions,
getUserThemes,
getSyntaxHighlightingThemes,
getSupportedLocales
};

View File

@ -90,8 +90,6 @@ async function register(app: express.Application) {
app.use(`/${assetPath}/node_modules/normalize.css/`, persistentCacheStatic(path.join(nodeModulesDir, "normalize.css/")));
app.use(`/${assetPath}/node_modules/jquery.fancytree/dist/`, persistentCacheStatic(path.join(nodeModulesDir, "jquery.fancytree/dist/")));
app.use(`/${assetPath}/node_modules/@highlightjs/cdn-assets/`, persistentCacheStatic(path.join(nodeModulesDir, "@highlightjs/cdn-assets/")));
}
export default {

View File

@ -217,7 +217,6 @@ function register(app: express.Application) {
apiRoute(PUT, "/api/options/:name/:value*", optionsApiRoute.updateOption);
apiRoute(PUT, "/api/options", optionsApiRoute.updateOptions);
apiRoute(GET, "/api/options/user-themes", optionsApiRoute.getUserThemes);
apiRoute(GET, "/api/options/codeblock-themes", optionsApiRoute.getSyntaxHighlightingThemes);
apiRoute(GET, "/api/options/locales", optionsApiRoute.getSupportedLocales);
apiRoute(PST, "/api/password/change", passwordApiRoute.changePassword);

View File

@ -1,22 +0,0 @@
import { describe, expect, it } from "vitest";
import { readThemesFromFileSystem } from "./code_block_theme.js";
import themeNames from "./code_block_theme_names.json" with { type: "json" };
import path = require("path");
describe("Code block theme", () => {
it("all themes are mapped", () => {
const themes = readThemesFromFileSystem(path.join(__dirname, "../../node_modules/@highlightjs/cdn-assets/styles"));
const mappedThemeNames = new Set(Object.values(themeNames));
const unmappedThemeNames = new Set<string>();
for (const theme of themes) {
if (!mappedThemeNames.has(theme.title)) {
unmappedThemeNames.add(theme.title);
}
}
expect(unmappedThemeNames.size, `Unmapped themes: ${Array.from(unmappedThemeNames).join(", ")}`).toBe(0);
});
});

View File

@ -1,107 +0,0 @@
/**
* @module
*
* Manages the server-side functionality of the code blocks feature, mostly for obtaining the available themes for syntax highlighting.
*/
import fs from "fs";
import themeNames from "./code_block_theme_names.json" with { type: "json" };
import { t } from "i18next";
import { join } from "path";
import { isDev, isElectron, getResourceDir } from "./utils.js";
/**
* Represents a color scheme for the code block syntax highlight.
*/
interface ColorTheme {
/** The ID of the color scheme which should be stored in the options. */
val: string;
/** A user-friendly name of the theme. The name is already localized. */
title: string;
}
/**
* Returns all the supported syntax highlighting themes for code blocks, in groups.
*
* The return value is an object where the keys represent groups in their human-readable name (e.g. "Light theme")
* and the values are an array containing the information about every theme. There is also a special group with no
* title (empty string) which should be displayed at the top of the listing pages, without a group.
*
* @returns the supported themes, grouped.
*/
export function listSyntaxHighlightingThemes() {
const path = getStylesDirectory();
const systemThemes = readThemesFromFileSystem(path);
return {
"": [
{
val: "none",
title: t("code_block.theme_none")
}
],
...groupThemesByLightOrDark(systemThemes)
};
}
export function getStylesDirectory() {
if (isElectron && !isDev) {
return join(getResourceDir(), "styles");
} else if (!isDev) {
return join(getResourceDir(), "node_modules/@highlightjs/cdn-assets/styles");
} else {
return join(__dirname, "../node_modules/@highlightjs/cdn-assets/styles");
}
}
/**
* Reads all the predefined themes by listing all minified CSSes from a given directory.
*
* The theme names are mapped against a known list in order to provide more descriptive names such as "Visual Studio 2015 (Dark)" instead of "vs2015".
*
* @param path the path to read from. Usually this is the highlight.js `styles` directory.
* @returns the list of themes.
*/
export function readThemesFromFileSystem(path: string): ColorTheme[] {
return fs
.readdirSync(path)
.filter((el) => el.endsWith(".min.css"))
.map((name) => {
const nameWithoutExtension = name.replace(".min.css", "");
let title = nameWithoutExtension.replace(/-/g, " ");
if (title in themeNames) {
title = (themeNames as Record<string, string>)[title];
}
return {
val: `default:${nameWithoutExtension}`,
title: title
};
});
}
/**
* Groups a list of themes by dark or light themes. This is done simply by checking whether "Dark" is present in the given theme, otherwise it's considered a light theme.
* This generally only works if the theme has a known human-readable name (see {@link #readThemesFromFileSystem()})
*
* @param listOfThemes the list of themes to be grouped.
* @returns the grouped themes by light or dark.
*/
function groupThemesByLightOrDark(listOfThemes: ColorTheme[]) {
const darkThemes = [];
const lightThemes = [];
for (const theme of listOfThemes) {
if (theme.title.includes("Dark")) {
darkThemes.push(theme);
} else {
lightThemes.push(theme);
}
}
const output: Record<string, ColorTheme[]> = {};
output[t("code_block.theme_group_light")] = lightThemes;
output[t("code_block.theme_group_dark")] = darkThemes;
return output;
}

View File

@ -1,82 +0,0 @@
{
"1c light": "1C (Light)",
"a11y dark": "a11y (Dark)",
"a11y light": "a11y (Light)",
"agate": "Agate (Dark)",
"an old hope": "An Old Hope (Dark)",
"androidstudio": "Android Studio (Dark)",
"arduino light": "Arduino (Light)",
"arta": "Arta (Dark)",
"ascetic": "Ascetic (Light)",
"atom one dark reasonable": "Atom One with ReasonML support (Dark)",
"atom one dark": "Atom One (Dark)",
"atom one light": "Atom One (Light)",
"brown paper": "Brown Paper (Light)",
"codepen embed": "CodePen Embed (Dark)",
"color brewer": "Color Brewer (Light)",
"cybertopia cherry": "Cybertopia Cherry (Dark)",
"cybertopia dimmer": "Cybertopia Dimmer (Dark)",
"cybertopia icecap": "Cybertopia Icecap (Dark)",
"cybertopia saturated": "Cybertopia Saturated (Dark)",
"dark": "Dark",
"default": "Original highlight.js Theme (Light)",
"devibeans": "devibeans (Dark)",
"docco": "Docco (Light)",
"far": "FAR (Dark)",
"felipec": "FelipeC (Dark)",
"foundation": "Foundation 4 Docs (Light)",
"github dark dimmed": "GitHub Dimmed (Dark)",
"github dark": "GitHub (Dark)",
"github": "GitHub (Light)",
"gml": "GML (Dark)",
"googlecode": "Google Code (Light)",
"gradient dark": "Gradient (Dark)",
"gradient light": "Gradient (Light)",
"grayscale": "Grayscale (Light)",
"hybrid": "hybrid (Dark)",
"idea": "Idea (Light)",
"intellij light": "IntelliJ (Light)",
"ir black": "IR Black (Dark)",
"isbl editor dark": "ISBL Editor (Dark)",
"isbl editor light": "ISBL Editor (Light)",
"kimbie dark": "Kimbie (Dark)",
"kimbie light": "Kimbie (Light)",
"lightfair": "Lightfair (Light)",
"lioshi": "Lioshi (Dark)",
"magula": "Magula (Light)",
"mono blue": "Mono Blue (Light)",
"monokai sublime": "Monokai Sublime (Dark)",
"monokai": "Monokai (Dark)",
"night owl": "Night Owl (Dark)",
"nnfx dark": "NNFX (Dark)",
"nnfx light": "NNFX (Light)",
"nord": "Nord (Dark)",
"obsidian": "Obsidian (Dark)",
"panda syntax dark": "Panda (Dark)",
"panda syntax light": "Panda (Light)",
"paraiso dark": "Paraiso (Dark)",
"paraiso light": "Paraiso (Light)",
"pojoaque": "Pojoaque (Dark)",
"purebasic": "PureBasic (Light)",
"qtcreator dark": "Qt Creator (Dark)",
"qtcreator light": "Qt Creator (Light)",
"rainbow": "Rainbow (Dark)",
"routeros": "RouterOS Script (Light)",
"rose pine dawn": "Rose Pine Dawn (Light)",
"rose pine moon": "Rose Pine Moon (Dark)",
"rose pine": "Rose Pine (Dark)",
"school book": "School Book (Light)",
"shades of purple": "Shades of Purple (Dark)",
"srcery": "Srcery (Dark)",
"stackoverflow dark": "Stack Overflow (Dark)",
"stackoverflow light": "Stack Overflow (Light)",
"sunburst": "Sunburst (Dark)",
"tokyo night dark": "Tokyo Night (Dark)",
"tokyo night light": "Tokyo Night (Light)",
"tomorrow night blue": "Tomorrow Night Blue (Dark)",
"tomorrow night bright": "Tomorrow Night Bright (Dark)",
"vs": "Visual Studio (Light)",
"vs2015": "Visual Studio 2015 (Dark)",
"xcode": "Xcode (Light)",
"xt256": "xt256 (Dark)"
}

View File

@ -1,6 +1,12 @@
"use strict";
import { parse, Renderer, type Tokens } from "marked";
import htmlSanitizer from "../html_sanitizer.js";
import importUtils from "./utils.js";
import { getMimeTypeFromMarkdownName, MIME_TYPE_AUTO } from "@triliumnext/commons";
import { ADMONITION_TYPE_MAPPINGS } from "../export/markdown.js";
import utils from "../utils.js";
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
/**
* Keep renderer code up to date with https://github.com/markedjs/marked/blob/master/src/Renderer.ts.
@ -123,14 +129,6 @@ class CustomMarkdownRenderer extends Renderer {
}
const renderer = new CustomMarkdownRenderer({ async: false });
import htmlSanitizer from "../html_sanitizer.js";
import importUtils from "./utils.js";
import { getMimeTypeFromHighlightJs, MIME_TYPE_AUTO, normalizeMimeTypeForCKEditor } from "./mime_type_definitions.js";
import { ADMONITION_TYPE_MAPPINGS } from "../export/markdown.js";
import utils from "../utils.js";
function renderToHtml(content: string, title: string) {
// Double-escape slashes in math expression because they are otherwise consumed by the parser somewhere.
content = content.replaceAll("\\$", "\\\\$");
@ -158,15 +156,17 @@ function renderToHtml(content: string, title: string) {
function getNormalizedMimeFromMarkdownLanguage(language: string | undefined) {
if (language) {
const highlightJsName = getMimeTypeFromHighlightJs(language);
if (highlightJsName) {
return normalizeMimeTypeForCKEditor(highlightJsName.mime);
const mimeDefinition = getMimeTypeFromMarkdownName(language);
if (mimeDefinition) {
return normalizeMimeTypeForCKEditor(mimeDefinition.mime);
}
}
return MIME_TYPE_AUTO;
}
const renderer = new CustomMarkdownRenderer({ async: false });
export default {
renderToHtml
};

View File

@ -1,223 +0,0 @@
// TODO: deduplicate with /src/public/app/services/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: "mips" },
{ 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 },
{ 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" },
{ 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" },
{ 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<string, MimeTypeDefinition> | 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];
}

View File

@ -22,7 +22,6 @@ function buildFilesToCopy() {
"autocomplete.js/dist",
"normalize.css/normalize.css",
"jquery.fancytree/dist",
"@highlightjs/cdn-assets",
// Required as they are native dependencies and cannot be well bundled.
"better-sqlite3",

View File

@ -21,6 +21,7 @@
* [Inconsistent Find and Replace Behavior in Large Code Notes](https://github.com/TriliumNext/Notes/issues/1826) by @SiriusXT
* [Incorrect import of multiple inline math](https://github.com/TriliumNext/Notes/pull/1906) by @SiriusXT
* [Random EPERM: operation not permitted on Windows](https://github.com/TriliumNext/Notes/issues/249)
* [The update button is sometimes blank](https://github.com/TriliumNext/Notes/pull/1975) by @SiriusXT
## ✨ Improvements
@ -32,10 +33,25 @@
* [Make it show which node triggered the event when right-clicking on tree](https://github.com/TriliumNext/Notes/pull/1861) by @SiriusXT
* [Only expand/collapse the left pane of the focused window](https://github.com/TriliumNext/Notes/pull/1905) by @SiriusXT
* Code notes:
* Added the GDScript (Godot) language.
* Added the GDScript (Godot) language for both code notes and code blocks in text notes.
* Added the Nix language (and also in code blocks for text notes).
* Added an indentation marker.
* Note: syntax highlighting for some languages (mostly HTML-template languages such as EJS, JSP) is no longer supported due to lack of upstream support. If this is a problem, feel free to report an issue and we can see what can be done about it.
* Syntax highlighting in code blocks for text notes:
* Added support for Cypher.
* Added support for XML-DTD.
* Added support for Jinja2.
* Added support for ClojureScript.
* Added support for Perl.
* Added support for Scala.
* Added support for Scheme.
* Added support for Swift.
* Added support for SystemVerilog.
* Added support for mIRC.
* Added support for Cobol.
* Added support for Dylan.
* Added support for RPM Specfile.
* Added support for TCCN3.
* Mermaid diagrams: basic syntax highlight (not all diagram types are supported) and code folding.
* Slight organization in Appearance settings: code block themes are now in "Text Notes", added a "Related settings" section in Appearance.
* [Added support for opening and activating a note in a new tab using Ctrl+Shift+click on notes in the launcher pane, note tree, or note images](https://github.com/TriliumNext/Notes/pull/1854) by @SiriusXT
@ -49,6 +65,7 @@
* Documented the new text note features: bookmarks and emojis.
* Add documentation links and updated pnpm commands to README by @perfectra1n
* Add documentation around setting the various environment variables to control upload size limit by @perfectra1n
* README improvements by @FliegendeWurst
## 🌍 Internationalization

View File

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

View File

@ -173,14 +173,6 @@ export default class SyntaxHighlighting extends Plugin {
return;
}
// Find the corresponding language for the given mimetype.
const highlightJsLanguage = this.config.mapLanguageName(mimeType);
if (mimeType !== this.config.defaultMimeType && !highlightJsLanguage) {
console.warn(`Unsupported highlight.js for mime type ${mimeType}.`);
return;
}
// Don't highlight if the code is too big, as the typing performance will be highly degraded.
if (codeBlock.childCount >= HIGHLIGHT_MAX_BLOCK_COUNT) {
return;
@ -230,8 +222,13 @@ export default class SyntaxHighlighting extends Plugin {
if (mimeType === this.config.defaultMimeType) {
highlightRes = this.hljs.highlightAuto(text);
} else {
highlightRes = this.hljs.highlight(text, { language: highlightJsLanguage });
highlightRes = this.hljs.highlight(text, { language: mimeType });
}
if (!highlightRes) {
return;
}
dbg("text\n" + text);
dbg("html\n" + highlightRes.value);

View File

@ -3,4 +3,5 @@ export * from "./lib/options_interface.js";
export * from "./lib/keyboard_actions_interface.js";
export * from "./lib/hidden_subtree.js";
export * from "./lib/rows.js";
export * from "./lib/test-utils.js"
export * from "./lib/test-utils.js";
export * from "./lib/mime_type.js";

View File

@ -0,0 +1,226 @@
/**
* 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. */
mdLanguageCode?: string;
/** If specified, will load the corresponding highlight file from the given path instead of `node_modules`. */
codeMirrorSource?: string;
}
export 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;
}
/**
* 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, "-");
}
/**
* 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", mdLanguageCode: "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", mdLanguageCode: "brainfuck" },
{ title: "C", mime: "text/x-csrc", mdLanguageCode: "c", default: true },
{ title: "C#", mime: "text/x-csharp", mdLanguageCode: "csharp", default: true },
{ title: "C++", mime: "text/x-c++src", mdLanguageCode: "cpp", default: true },
{ title: "Clojure", mime: "text/x-clojure", mdLanguageCode: "clojure" },
{ title: "ClojureScript", mime: "text/x-clojurescript" },
{ title: "Closure Stylesheets (GSS)", mime: "text/x-gss" },
{ title: "CMake", mime: "text/x-cmake", mdLanguageCode: "cmake" },
{ title: "Cobol", mime: "text/x-cobol" },
{ title: "CoffeeScript", mime: "text/coffeescript", mdLanguageCode: "coffeescript" },
{ title: "Common Lisp", mime: "text/x-common-lisp", mdLanguageCode: "lisp" },
{ title: "CQL", mime: "text/x-cassandra" },
{ title: "Crystal", mime: "text/x-crystal", mdLanguageCode: "crystal" },
{ title: "CSS", mime: "text/css", mdLanguageCode: "css", default: true },
{ title: "Cypher", mime: "application/x-cypher-query" },
{ title: "Cython", mime: "text/x-cython" },
{ title: "D", mime: "text/x-d", mdLanguageCode: "d" },
{ title: "Dart", mime: "application/dart", mdLanguageCode: "dart" },
{ title: "diff", mime: "text/x-diff", mdLanguageCode: "diff" },
{ title: "Django", mime: "text/x-django", mdLanguageCode: "django" },
{ title: "Dockerfile", mime: "text/x-dockerfile", mdLanguageCode: "dockerfile" },
{ title: "DTD", mime: "application/xml-dtd" },
{ title: "Dylan", mime: "text/x-dylan" },
{ title: "EBNF", mime: "text/x-ebnf", mdLanguageCode: "ebnf" },
{ title: "ECL", mime: "text/x-ecl" },
{ title: "edn", mime: "application/edn" },
{ title: "Eiffel", mime: "text/x-eiffel" },
{ title: "Elm", mime: "text/x-elm", mdLanguageCode: "elm" },
{ title: "Embedded Javascript", mime: "application/x-ejs" },
{ title: "Embedded Ruby", mime: "application/x-erb", mdLanguageCode: "erb" },
{ title: "Erlang", mime: "text/x-erlang", mdLanguageCode: "erlang" },
{ title: "Esper", mime: "text/x-esper" },
{ title: "F#", mime: "text/x-fsharp", mdLanguageCode: "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", mdLanguageCode: "fortran" },
{ title: "Gas", mime: "text/x-gas" },
{ title: "GDScript (Godot)", mime: "text/x-gdscript" },
{ title: "Gherkin", mime: "text/x-feature", mdLanguageCode: "gherkin" },
{ title: "GitHub Flavored Markdown", mime: "text/x-gfm", mdLanguageCode: "markdown" },
{ title: "Go", mime: "text/x-go", mdLanguageCode: "go", default: true },
{ title: "Groovy", mime: "text/x-groovy", mdLanguageCode: "groovy", default: true },
{ title: "HAML", mime: "text/x-haml", mdLanguageCode: "haml" },
{ title: "Haskell (Literate)", mime: "text/x-literate-haskell" },
{ title: "Haskell", mime: "text/x-haskell", mdLanguageCode: "haskell", default: true },
{ title: "Haxe", mime: "text/x-haxe", mdLanguageCode: "haxe" },
{ title: "HTML", mime: "text/html", mdLanguageCode: "xml", default: true },
{ title: "HTTP", mime: "message/http", mdLanguageCode: "http", default: true },
{ title: "HXML", mime: "text/x-hxml" },
{ title: "IDL", mime: "text/x-idl" },
{ title: "Java Server Pages", mime: "application/x-jsp", mdLanguageCode: "java" },
{ title: "Java", mime: "text/x-java", mdLanguageCode: "java", default: true },
{ title: "Jinja2", mime: "text/jinja2" },
{ title: "JS backend", mime: "application/javascript;env=backend", mdLanguageCode: "javascript", default: true },
{ title: "JS frontend", mime: "application/javascript;env=frontend", mdLanguageCode: "javascript", default: true },
{ title: "JSON-LD", mime: "application/ld+json", mdLanguageCode: "json" },
{ title: "JSON", mime: "application/json", mdLanguageCode: "json", default: true },
{ title: "JSX", mime: "text/jsx", mdLanguageCode: "javascript" },
{ title: "Julia", mime: "text/x-julia", mdLanguageCode: "julia" },
{ title: "Kotlin", mime: "text/x-kotlin", mdLanguageCode: "kotlin", default: true },
{ title: "LaTeX", mime: "text/x-latex", mdLanguageCode: "latex" },
{ title: "LESS", mime: "text/x-less", mdLanguageCode: "less" },
{ title: "LiveScript", mime: "text/x-livescript", mdLanguageCode: "livescript" },
{ title: "Lua", mime: "text/x-lua", mdLanguageCode: "lua" },
{ title: "MariaDB SQL", mime: "text/x-mariadb", mdLanguageCode: "sql" },
{ title: "Markdown", mime: "text/x-markdown", mdLanguageCode: "markdown", default: true },
{ title: "Mathematica", mime: "text/x-mathematica", mdLanguageCode: "mathematica" },
{ title: "mbox", mime: "application/mbox" },
{ title: "MIPS Assembler", mime: "text/x-asm-mips", mdLanguageCode: "mips" },
{ title: "mIRC", mime: "text/mirc" },
{ title: "Modelica", mime: "text/x-modelica" },
{ title: "MS SQL", mime: "text/x-mssql", mdLanguageCode: "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", mdLanguageCode: "sql" },
{ title: "Nix", mime: "text/x-nix", mdLanguageCode: "nix" },
{ title: "Nginx", mime: "text/x-nginx-conf", mdLanguageCode: "nginx" },
{ title: "NSIS", mime: "text/x-nsis", mdLanguageCode: "nsis" },
{ title: "NTriples", mime: "application/n-triples" },
{ title: "Objective-C", mime: "text/x-objectivec", mdLanguageCode: "objectivec" },
{ title: "OCaml", mime: "text/x-ocaml", mdLanguageCode: "ocaml" },
{ title: "Octave", mime: "text/x-octave" },
{ title: "Oz", mime: "text/x-oz" },
{ title: "Pascal", mime: "text/x-pascal", mdLanguageCode: "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 },
{ title: "Pig", mime: "text/x-pig" },
{ title: "PLSQL", mime: "text/x-plsql", mdLanguageCode: "sql" },
{ title: "PostgreSQL", mime: "text/x-pgsql", mdLanguageCode: "pgsql" },
{ title: "PowerShell", mime: "application/x-powershell", mdLanguageCode: "powershell" },
{ title: "Properties files", mime: "text/x-properties", mdLanguageCode: "properties" },
{ title: "ProtoBuf", mime: "text/x-protobuf", mdLanguageCode: "protobuf" },
{ title: "Pug", mime: "text/x-pug" },
{ title: "Puppet", mime: "text/x-puppet", mdLanguageCode: "puppet" },
{ title: "Python", mime: "text/x-python", mdLanguageCode: "python", default: true },
{ title: "Q", mime: "text/x-q", mdLanguageCode: "q" },
{ title: "R", mime: "text/x-rsrc", mdLanguageCode: "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", mdLanguageCode: "ruby", default: true },
{ title: "Rust", mime: "text/x-rustsrc", mdLanguageCode: "rust" },
{ title: "SAS", mime: "text/x-sas", mdLanguageCode: "sas" },
{ title: "Sass", mime: "text/x-sass" },
{ title: "Scala", mime: "text/x-scala" },
{ title: "Scheme", mime: "text/x-scheme" },
{ title: "SCSS", mime: "text/x-scss", mdLanguageCode: "scss" },
{ title: "Shell (bash)", mime: "text/x-sh", mdLanguageCode: "bash", default: true },
{ title: "Sieve", mime: "application/sieve" },
{ title: "Slim", mime: "text/x-slim" },
{ title: "Smalltalk", mime: "text/x-stsrc", mdLanguageCode: "smalltalk" },
{ title: "Smarty", mime: "text/x-smarty" },
{ title: "SML", mime: "text/x-sml", mdLanguageCode: "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", mdLanguageCode: "sql", default: true },
{ title: "SQLite (Trilium)", mime: "text/x-sqlite;schema=trilium", mdLanguageCode: "sql", default: true },
{ title: "SQLite", mime: "text/x-sqlite", mdLanguageCode: "sql" },
{ title: "Squirrel", mime: "text/x-squirrel" },
{ title: "sTeX", mime: "text/x-stex" },
{ title: "Stylus", mime: "text/x-styl", mdLanguageCode: "stylus" },
{ title: "Swift", mime: "text/x-swift", default: true },
{ title: "SystemVerilog", mime: "text/x-systemverilog" },
{ title: "Tcl", mime: "text/x-tcl", mdLanguageCode: "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", mdLanguageCode: "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", mdLanguageCode: "twig" },
{ title: "TypeScript-JSX", mime: "text/typescript-jsx" },
{ title: "TypeScript", mime: "application/typescript", mdLanguageCode: "typescript" },
{ title: "VB.NET", mime: "text/x-vb", mdLanguageCode: "vbnet" },
{ title: "VBScript", mime: "text/vbscript", mdLanguageCode: "vbscript" },
{ title: "Velocity", mime: "text/velocity" },
{ title: "Verilog", mime: "text/x-verilog", mdLanguageCode: "verilog" },
{ title: "VHDL", mime: "text/x-vhdl", mdLanguageCode: "vhdl" },
{ title: "Vue.js Component", mime: "text/x-vue" },
{ title: "Web IDL", mime: "text/x-webidl" },
{ title: "XML", mime: "text/xml", mdLanguageCode: "xml", default: true },
{ title: "XQuery", mime: "application/xquery", mdLanguageCode: "xquery" },
{ title: "xu", mime: "text/x-xu" },
{ title: "Yacas", mime: "text/x-yacas" },
{ title: "YAML", mime: "text/x-yaml", mdLanguageCode: "yaml", default: true },
{ title: "Z80", mime: "text/x-z80" }
]);
let byMarkdownNameMappings: Record<string, MimeTypeDefinition> | null = null;
/**
* Given a Markdown 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 mdLanguageCode a language tag.
* @returns the corresponding {@link MimeTypeDefinition} if found, or `undefined` otherwise.
*/
export function getMimeTypeFromMarkdownName(mdLanguageCode: string) {
if (!byMarkdownNameMappings) {
byMarkdownNameMappings = {};
for (const mimeType of MIME_TYPES_DICT) {
if (mimeType.mdLanguageCode && !byMarkdownNameMappings[mimeType.mdLanguageCode]) {
byMarkdownNameMappings[mimeType.mdLanguageCode] = mimeType;
}
}
}
return byMarkdownNameMappings[mdLanguageCode];
}

View File

@ -2,22 +2,18 @@
import { defineConfig } from 'vite';
export default defineConfig(() => ({
root: __dirname,
cacheDir: '../../node_modules/.vite/packages/commons',
plugins: [],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
test: {
'watch': false,
'globals': true,
'environment': "node",
'include': ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
'reporters': ["default"],
'coverage': {
'reportsDirectory': './test-output/vitest/coverage',
'provider': 'v8' as const,
}
},
root: __dirname,
cacheDir: '../../node_modules/.vite/packages/commons',
plugins: [],
test: {
'watch': false,
'globals': true,
'environment': "node",
'include': ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
'reporters': ["default"],
'coverage': {
'reportsDirectory': './test-output/vitest/coverage',
'provider': 'v8' as const,
}
},
}));

View File

@ -0,0 +1,11 @@
# highlightjs
This library was generated with [Nx](https://nx.dev).
## Building
Run `nx build highlightjs` to build the library.
## Running unit tests
Run `nx test highlightjs` to execute the unit tests via [Vitest](https://vitest.dev/).

View File

@ -0,0 +1,24 @@
import baseConfig from "../../eslint.config.mjs";
export default [
...baseConfig,
{
"files": [
"**/*.json"
],
"rules": {
"@nx/dependency-checks": [
"error",
{
"ignoredFiles": [
"{projectRoot}/eslint.config.{js,cjs,mjs}",
"{projectRoot}/vite.config.{js,ts,mjs,mts}"
]
}
]
},
"languageOptions": {
"parser": (await import('jsonc-eslint-parser'))
}
}
];

View File

@ -0,0 +1,28 @@
{
"name": "@triliumnext/highlightjs",
"version": "0.0.1",
"private": true,
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"development": "./src/index.ts",
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"nx": {
"name": "highlightjs"
},
"dependencies": {
"@exercism/highlightjs-gdscript": "0.0.1",
"@triliumnext/commons": "workspace:*",
"highlight.js": "11.11.1",
"highlightjs-cobol": "0.3.3",
"highlightjs-cypher": "1.2.0"
}
}

View File

@ -0,0 +1,67 @@
import hljs from "../node_modules/highlight.js/es/core.js";
import { normalizeMimeTypeForCKEditor, type MimeType } from "@triliumnext/commons";
import syntaxDefinitions from "./syntax_highlighting.js";
import { type Theme } from "./themes.js";
import { type HighlightOptions } from "highlight.js";
export { default as Themes, type Theme } from "./themes.js";
const registeredMimeTypes = new Set<string>();
const unsupportedMimeTypes = new Set<string>();
let highlightingThemeEl: HTMLStyleElement | null = null;
export async function ensureMimeTypes(mimeTypes: MimeType[]) {
for (const mimeType of mimeTypes) {
if (!mimeType.enabled) {
continue;
}
const mime = normalizeMimeTypeForCKEditor(mimeType.mime);
if (registeredMimeTypes.has(mime)) {
continue;
}
registeredMimeTypes.add(mime);
const loader = syntaxDefinitions[mime];
if (!loader) {
unsupportedMimeTypes.add(mime);
continue;
}
const language = (await loader()).default;
hljs.registerLanguage(mime, language);
}
}
export function highlight(code: string, options: HighlightOptions) {
if (unsupportedMimeTypes.has(options.language)) {
return null;
}
if (!registeredMimeTypes.has(options.language)) {
console.warn(`Unable to find highlighting for ${options.language}.`);
return null;
}
return hljs.highlight(code, options);
}
export async function loadTheme(theme: "none" | Theme) {
if (theme === "none") {
if (highlightingThemeEl) {
highlightingThemeEl.remove();
highlightingThemeEl = null;
}
return;
}
if (!highlightingThemeEl) {
highlightingThemeEl = document.createElement("style");
document.querySelector("head")?.append(highlightingThemeEl);
}
const themeCss = (await theme.load()).default as string;
highlightingThemeEl.textContent = themeCss;
}
export const { highlightAuto } = hljs;

View File

@ -0,0 +1,76 @@
import type { HLJSApi, Language, Mode } from "highlight.js";
/*
* highlight.js Dylan syntax highlighting definition
*
* Source: https://github.com/highlightjs/highlightjs-dylan/blob/master/src/dylan.js
*
* @see https://github.com/highlightjs/highlight.js
* @see https://opendylan.org/
*
* :TODO:
*
* @package: highlightjs-dylan
* @author: Peter Hull <peterhull90@gmail.com>
* @since: 2019-04-08
*
* Description: Dylan language definition
* Category: functional
*/
export default function(hljs: HLJSApi): Language {
const DYLAN_CORE_WORDS = ["define", "end", "handler", "let", "local", "macro", "otherwise"];
const DYLAN_BEGIN_WORDS = ["begin", "block", "case", "for", "if", "method",
"select", "unless", "until", "while"
];
const DYLAN_FUNCTION_WORDS: string[] = [];
const DYLAN_DEFINE_BODY_WORDS = ["class", "library", "method", "module"];
const DYLAN_DEFINE_LIST_WORDS = ["constant", "variable", "domain"];
const DYLAN_RESERVED_WORDS = ([] as string[]).concat(DYLAN_CORE_WORDS,
DYLAN_BEGIN_WORDS,
DYLAN_FUNCTION_WORDS,
DYLAN_DEFINE_BODY_WORDS.map(function(word) {
return word + '|2';
}),
DYLAN_DEFINE_LIST_WORDS
);
const DYLAN_HASH_WORDS = ["#t", "#f", "#next", "#rest", "#key", "#all-keys", "#include"];
const DYLAN_WORD = '[a-z\-+\*/^=#!%$_><@\?~][a-z0-9\-+\*/^=#!%$_><@\?~]*';
const KEYWORDS = {
$pattern: DYLAN_WORD,
literal: DYLAN_HASH_WORDS.join(" "),
keyword: DYLAN_RESERVED_WORDS.join(" ")
};
const DYLAN_CODE = {
case_insensitive: true,
className: 'dylan',
keywords: KEYWORDS,
contains: [{
className: 'class',
begin: '<' + DYLAN_WORD + '>',
relevance: 0
},
{
className: 'symbol',
begin: '#' + DYLAN_WORD
},
{
className: 'symbol',
begin: DYLAN_WORD + ':',
relevance: 0
},
{
className: 'string',
begin: '\'\\\\?.',
end: '\'',
illegal: '.'
},
hljs.C_NUMBER_MODE,
hljs.QUOTE_STRING_MODE,
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE
]
};
return DYLAN_CODE;
};

View File

@ -0,0 +1,253 @@
import type { HLJSApi, Language, Mode } from "highlight.js";
/**
* highlight.js mIRC Scripting Language syntax highlighting definition
*
* Source: https://github.com/highlightjs/highlightjs-mirc/blob/master/mirc.js
*
* @see https://github.com/isagalaev/highlight.js
*
* @package: highlightjs-mirc
* @author: Kedyn Macedonio <mkedyn@gmail.com>
* @author: Sven Roelse <acvxqs@icloud.com>
*/
export default function hljsDefineMIRC(hljs: HLJSApi): Language {
const COMMENT_DOC = {
className: 'comment',
begin: /^\x20*\/\*\*\s+/,
end: /^\x20*\*\//,
contains: [
{
className: 'doctag',
begin: /@\w+/
}
]
};
const COMMENT_BLOCK = {
className: 'comment',
begin: /^\x20*\/\*/,
end: /^\x20*\*\//
};
const COMMENT_LINE = {
className: 'comment',
begin: /(^\x20*|}\x20+|\x20+\|\x20+);/,
end: /(\x20+\|\x20+.+|$)/,
excludeEnd: true
};
const VARIABLES = {
className: 'variable',
begin: /%[^\s,\)]+/
};
const IDENTIFIERS = [
{
className: 'literal',
begin: /\$\$?(true|false|null)\b/,
},
{
className: 'built_in',
begin: /\$\$?(\!|0|(?:[1-9](?:\d+)?-?(?:\d+)?|\?\d+)|\+|\?|(?:abook|abs|acos|active(cid|wid)?|adate|address|addtok(cs)?|agent(name|stat|ver)?|alias|and|anick|ansi2mirc|aop|appactive|appstate|asc(time)?|asin|atan2?|auto|avoice|away(msg|time)?|banmask|banlist|base|beta|bfind|bindip|bitoff|biton|bits|bnick|bvar|bytes|calc|caller|cancel|cb|cd|ceil|chan(modes|nel|types)?|chat|chr|cid|clevel|click|cmdbox|cmdline|cnick|color|com(call|chan|char|err|pact|press|val)?|cosh?|count(cs)?|crc?|creq|crlf|ctimer?|ctrlenter|date|day(light)?|dbuh|dbuw|dcc(ignore|port)|dde(name)?|debug|decode|decompress|deltok|devent|dialog|did(reg|tok|wm)?|dir=|disk|dlevel|dll(call)?|dname|dns|dqwindow|duration|ebeeps|editbox|email(addr)?|encode|envvar|error|eval(next)?|event(id|params)?|exist(s|ing)|feof|ferr|fgetc|file(=|name)?|filtered|finddirn?|findfilen?|findtok(cs)?|fline|floor|font|fopen|fread|fromeditbox|fserve|full(address|date|name|screen)|get(dir|dot|tok)?|gmt|group|halted|hash|height|hfile=?|hfind|hget|highlight|hmac|hmatch|hnick|host|hotline(pos)?|hotlink|hotp|hregex|hypot|iaddress|ial(chan)?|ibl|idle|iel|ifmatch2?|ignore|iif|iil|inellipse|ini(topic)?|in(midi|mode|mp3|paste|poly|put|(round)rect|song|stok|t(ersect)?|wave|who)|ip(type)?|iql|is(alias|bit|dde|dir|file|id|lower|tok(cs)?|upper|utf)|key(char|rpt|val)|knick|lactive(cid|wid)?|left(win|wincid|winwid)?|len|level|lf|lines?|link|lock(ed)?|lof|log(10|dir|stamp(fmt)?)?|long(fn|ip)|lower|ltimer|maddress|mask|match(key|tok(cs)?)|maxlen(s|m|l)|md5|menu(bar|context|type)?|me|mid(idir)?|mircdir|mircexe|mircini|mk(log)?fn|mknickfn|mnick|mode(first|last|spl)?|mouse|mp3|msfile|msgstamp|msgtags|naddress|network|newnick|nhnick|nick(mode)?|no(file|path|pnick|qt|tags|tify|t)?|numeric|numtok|nvnick|ok|online(server|total)?|onpoly|opnick|ord?|os|parms|parse(line|type|utf)|passivedcc|pic?|play|pnick|portable|portfree|pos(cs)?|prefix|prop|protect|puttok|qt|query|r(address|ands?)?|raw(bytes|msg)|read(ini|n)?|reg(br|errstr|ex|ml(ex)?|sub(ex)?)|rem(ote|move(cs)?|tok(cs)?)|replace(cs|xcs|x)?|reptok(cs)?|result|rgb|right|rnick|round|samepath|scid|scon|script(dir([^\s\(),><:"|?*]+)?|line)?|sdir|send|server(ip|target)?|sfile|sha1|sha(256|384|512)|shortfn|show|signal|sinh?|site|sline|snick(s)?|snotify|sock(br|err|name)?|sorttok(cs)?|sound|speak|sqrt|sreq|ssl(certsha1|certsha256)?|ssl((lib)?dll)?|ssl(ready|version)|starting|status|str(ipped|ip)?|style|submenu|switchbar|sysdir|tanh?|target|tempfn|ticks|time(out|stamp(fmt)?r|zone)?|tips?|titlebar|token|toolbar|topic|totp|treebar|trust|ulevel|ulist|unsafe|upper|uptime|url|usermode|utf(de|en)code|v1|v2|var|vc|vcmd(stat|ver)?|version|vnick|vol|wavdir|wid(th)?|wild(site|tok(cs)?)|window|wrap|xor|yes|zip)\b)/
}
];
const COMMANDS = {
className: 'built_in',
begin: /\b(\/)?([!.]{1,2})?(abook|ajinvite|alias|aline|ame|amsg|anick|aop|auser|autojoin|avoice|away|background|ban|bcopy|beep|bindip|bread|breplace|bset|btrunc|bunset|bwrite|channel|clear(all)?|cline|clipboard|close|cnick|color|colour|com(close|list|open|reg)|copy|creq|ctcp(reply|s)?|dcc(server)?|dde(server)?|debug|dec|describe|dialog|did(tok)?|disable|disconnect|dlevel|dline|dll|dns|dqwindow|draw(copy|dot|fill|line|pic|rect|replace|rot|save|scroll|size|text)|ebeeps|echo|editbox|emailaddr|enable|events|exit|fclose|filter|findtext|finger|firewall|flash|flist|flood|flush|flushini|fnord|font|fopen|fseek|fsend|fserve|fullname|fupdate|fwrite|ghide|gload|gmove|gopts|gplay|gpoint|gqreq|groups|gshow|gsize|gstop|gtalk|gunload|guser|hadd|hdec|hdel|help|hfree|hinc|hload|hmake|hop|hotlink|hsave|ial(clear|mark)?|identd|ignore|iline|inc|iuser|join|leave|linesep|links|list|load(buf)?|localinfo|log|logview|mdi|me|menubar|mkdir|mnick|mode|msg|noop|notice|notify|omsg|onotice|parseline|part|partall|pdcc|perform|play|playctrl|pop|privmsg|protect|proxy|pvoice|qme|qmsg|query|queryrn|quit|raw|registration|reload|remini|remote|remove|rename|renwin|reseterror|resetidle|rlevel|rline|rmdir|run|ruser|save(buf|ini)?|say|scid|scon|server|set(layer)?|showmirc|signal|sline|sock(accept|close|list|listen|mark|open|pause|read|rename|udp|write)|sound|speak|splay|sreq|strip|switchbar|timer([^\x20]+)?|timestamp|tips?|titlebar|tnick|tokenize|toolbar|tray|treebar|ulist|unload|unset(all)?|updatenl|url|uwho|var|vc(add|md|rem)|vmsg|vnotice|vol|wall(chops|voices)|window|winhelp|write(ini)?|xyzzy)\b/,
}
const ALIAS_DECLARATION = {
className: 'function',
begin: /^alias(\x20+-l)?\x20+[^\s]+/,
returnBegin: true,
contains: [
{
className: 'type',
begin: /^alias/
},
{
className: 'symbol',
begin: /\x20+-l/
},
{
className: 'title',
begin: /\x20+[^\s]+/,
endsParent: true
}
]
};
const DIALOG = {
className: 'code',
begin: /^dialog(\x20+-l)?\x20+[^\x20]+\x20+{$/,
end: /^}$/,
returnBegin: true,
contains: [
{
className: 'built_in',
begin: /^dialog(\x20+-l)?\x20+[^\x20]+\x20+/,
end: /{$/,
excludeEnd: true,
returnBegin: true,
contains: [
{
className: 'symbol',
begin: /\x20+-l/,
},
{
className: 'title',
begin: /\x20+[^\x20]+\x20+/,
end: /{$/,
excludeEnd: true,
endsParent: true,
}
]
},
{
className: 'keyword',
begin: /^\x20+(title|icon|size|option|text|edit|button|check|radio|box|scroll|list|combo|link|tab|menu|item)\x20+/
},
hljs.QUOTE_STRING_MODE,
hljs.NUMBER_MODE,
COMMENT_BLOCK,
COMMENT_LINE,
VARIABLES,
IDENTIFIERS[0],
IDENTIFIERS[1]
]
};
const MENU = {
className: 'code',
begin: /^menu\x20+[^\x20]+\x20*/,
end: /{/,
returnBegin: true,
excludeEnd: true,
contains: [
{
className: 'built_in',
begin: /^menu\b/,
end: /\x20+/,
excludeEnd: true
},
{
className: 'title',
begin: /[^\s]+\x20*/,
endsParent: true
}
]
};
const GROUPS = {
className: 'symbol',
begin: /^#[^\s]+\x20+(on|off|end)$/
}
const EVENTS1 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:(action|notice|text):[^:]+:(\?|#[^:]*|\*):/
};
const EVENTS2 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:(active|input|tabcomp):(\*|#[^:]*|\?|=|!|@[^:]*):/
};
const EVENTS3 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:(agent|appactive|(dis)?connect(fail)?|dns|exit|(un)?load|(midi|mp3|play|wave)end|nick|nosound|u?notify|ping|pong|quit|start|usermode):/
};
const EVENTS4 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:((un)?ban|(de)?help|(de|server)?op|(de)?owner|(de)?voice|invite|join|kick|(server)?mode|part|rawmode|topic):(?:\*|#[^:]*):/
};
const EVENTS5 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:(chat|ctcpreply|error|file(rcvd|sent)|(get|send)fail|logon|serv|signal|snotice|sock(close|listen|open|read|write)|udpread|vcmd|wallops):[^:]+:/
};
const EVENTS6 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:close:(\*|\?|=|!|@[^:]*):/
};
const EVENTS7 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:dccserver:(chat|send|fserve):/
};
const EVENTS8 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:dialog:[^:]+:(init|close|edit|sclick|dclick|menu|scroll|mouse|rclick|drop|\*):[\d\-,\*]+:/
};
const EVENTS9 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:hotlink:[^:]+:(?:\*|#[^:]*|\?|=|!|@[^:]*):/
};
const EVENTS10 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:key(down|up):(\*|@[^:]*):(\*|\d+(,\d+)*):/
};
const EVENTS11 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:open:(\*|\?|=|!|@[^:]*):[^:]+:/
};
const EVENTS12 = {
className: 'built_in',
begin: /^on\x20+(me:)?[^:\x20]+:parseline:(\*|in|out):[^:]+:/
};
const EVENTS13 = {
className: 'built_in',
begin: /^raw\x20+(me:)?[^:\x20]+:[^:]+:/
};
const EVENTS14 = {
className: 'built_in',
begin: /^ctcp\x20+(me:)?[^:\x20]+:[^:]+:(\*|#.*|\?):/
};
return {
aliases: ['mrc'],
keywords: 'if elseif else while break continue halt haltdef goto return returnex',
case_insensitive: true,
contains: [
COMMENT_DOC,
COMMENT_BLOCK,
COMMENT_LINE,
hljs.NUMBER_MODE,
ALIAS_DECLARATION,
DIALOG,
MENU,
GROUPS,
EVENTS1,
EVENTS2,
EVENTS3,
EVENTS4,
EVENTS5,
EVENTS6,
EVENTS7,
EVENTS8,
EVENTS9,
EVENTS10,
EVENTS11,
EVENTS12,
EVENTS13,
EVENTS14,
VARIABLES,
IDENTIFIERS[0],
IDENTIFIERS[1],
COMMANDS
]
};
}

View File

@ -0,0 +1,71 @@
import type { HLJSApi, Language, Mode } from "highlight.js";
/**
* highlight.js RPM spec file syntax highlighting definition
*
* Source: https://github.com/highlightjs/highlightjs-rpm-specfile
*
* @see https://github.com/highlightjs/highlight.js
*
* @package highlightjs-rpm-specfile
* @author Ryan Lerch <rlerch@redhat.com>, Neal Gompa <ngompa13@gmail.com>
* @since 2019-07-08
* @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD-3-Clause
*
* Language: rpm-specfile
* Description: RPM Specfile
* Author: Ryan Lerch <rlerch@redhat.com>
* Contributors: Neal Gompa <ngompa13@gmail.com>
* Category: config
* Requires: bash.js
* Website: https://rpm.org/
**/
export default function hljsDefineRpmSpecfile(hljs: HLJSApi): Language {
return {
aliases: ['rpm', 'spec', 'rpm-spec', 'specfile'],
contains: [
hljs.COMMENT('%dnl', '$'),
hljs.HASH_COMMENT_MODE,
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
{
className: "type",
begin: /^(Name|BuildRequires|Version|Release|Epoch|Summary|Group|License|Packager|Vendor|Icon|URL|Distribution|Prefix|Patch[0-9]*|Source[0-9]*|Requires\(?[a-z]*\)?|[a-z]+Req|Obsoletes|Recommends|Suggests|Supplements|Enhances|Provides|Conflicts|RemovePathPostfixes|Build[a-z]+|[a-z]+Arch|Auto[a-z]+)(:)/,
},
{
className: "keyword",
begin: /(%)(?:package|prep|generate_buildrequires|sourcelist|patchlist|build|description|install|verifyscript|clean|changelog|check|pre[a-z]*|post[a-z]*|trigger[a-z]*|files)/,
},
{
className: "link",
begin: /(%)(if|ifarch|ifnarch|ifos|ifnos|elif|elifarch|elifos|else|endif)/,
},
{
className: "link",
begin: /%\{_/,
end: /}/,
},
{
className: "symbol",
begin: /%\{\?/,
end: /}/,
},
{
className: "link font-weight-bold",
begin: /%\{/,
end: /}/,
},
{
className: "link font-weight-bold",
begin: /%/,
end: /[ \t\n]/
},
{
className: "symbol font-weight-bold",
begin: /^\* (Mon|Tue|Wed|Thu|Fri|Sat|Sun)/,
end: /$/,
},
]
};
}

View File

@ -0,0 +1,96 @@
/*
* highlight.js terraform syntax highlighting definition
*
* @see https://github.com/highlightjs/highlight.js
*
* :TODO:
*
* @package: highlightjs-terraform
* @author: Nikos Tsirmirakis <nikos.tsirmirakis@winopsdba.com>
* @since: 2019-03-20
*
* Description: Terraform (HCL) language definition
* Category: scripting
*/
import type { HLJSApi, Language, Mode } from "highlight.js";
export default function hljsDefineTerraform(hljs: HLJSApi): Language {
const NUMBERS: Mode = {
className: 'number',
begin: '\\b\\d+(\\.\\d+)?',
relevance: 0
};
const STRINGS: Mode = {
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' as const]
}]
}]
};
return {
aliases: ['tf', 'hcl'],
keywords: {
keyword: [
"resource",
"variable",
"provider",
"output",
"locals",
"module",
"data",
"terraform|10"
],
literal: [
"false",
"true",
"null"
]
},
contains: [
hljs.COMMENT('\\#', '$'),
NUMBERS,
STRINGS
]
}
}

View File

@ -0,0 +1,343 @@
import type { HLJSApi, Language, Mode } from "highlight.js";
/* vim:set ts=2 sw=2 et: */
/*
Source: https://gitea.osmocom.org/ttcn3/highlightjs-ttcn3/src/branch/master/ttcn3.js
Language: TTCN-3
Description: TTCN-3 is a domain specific programming language used particularly (but not only) in the telecom domain.
Website: http://www.ttcn-3.org/
Category: common, protocols
Spec: ETSI ES 201 873-1 V4.15.1
Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
*/
export default function(hljs: HLJSApi): Language {
const RESERVED_WORDS = [ /* table A.3 */
'action',
'activate',
'address',
'alive',
'all',
'alt',
'altstep',
'and',
'and4b',
'any',
'anytype',
'bitstring',
'boolean',
'break',
'case',
'call',
'catch',
'char',
'charstring',
'check',
'clear',
'complement',
'component',
'connect',
'const',
'continue',
'control',
'create',
'deactivate',
'decmatch',
'default',
'disconnect',
'display',
'do',
'done',
'else',
'encode',
'enumerated',
'error',
'except',
'exception',
'execute',
'extends',
'extension',
'external',
'fail',
'false',
'float',
'for',
'friend',
'from',
'function',
'getverdict',
'getcall',
'getreply',
'goto',
'group',
'halt',
'hexstring',
'if',
'ifpresent',
'import',
'in',
'inconc',
'infinity',
'inout',
'integer',
'interleave',
'isbound',
'ischosen',
'ispresent',
'isvalue',
'kill',
'killed',
'label',
'language',
'length',
'log',
'map',
'match',
'message',
'mixed',
'mod',
'modifies',
'module',
'modulepar',
'mtc',
'noblock',
'none',
'not',
'not_a_number',
'not4b',
'nowait',
'null',
'octetstring',
'of',
'omit',
'on',
'optional',
'or',
'or4b',
'out',
'override',
'param',
'pass',
'pattern',
'permutation',
'port',
'present',
'private',
'procedure',
'public',
'raise',
'read',
'receive',
'record',
'recursive',
'rem',
'repeat',
'reply',
'return',
'running',
'runs',
'select',
'self',
'send',
'sender',
'set',
'setencode',
'setverdict',
'signature',
'start',
'stop',
'subset',
'superset',
'system',
'template',
'testcase',
'timeout',
'timer',
'to',
'trigger',
'true',
'type',
'union',
'universal',
'unmap',
'value',
'valueof',
'var',
'variant',
'verdicttype',
'while',
'with',
'xor',
'xor4b',
];
const BUILT_INS = [
'action',
'activate',
'any2unistr',
'bit2hex',
'bit2int',
'bit2oct',
'bit2str',
'call',
'catch',
'char2int',
'char2oct',
'check',
'clear',
'complement',
'connect',
'create',
'deactivate',
'decmatch',
'decvalue',
'decvalue_o',
'decvalue_unichar',
'disconnect',
'encode',
'encvalue',
'encvalue_o',
'encvalue_unichar',
'enum2int',
'execute',
'float2int',
'get_stringencoding',
'getcall',
'getreply',
'getverdict',
'halt',
'hex2bit',
'hex2int',
'hex2oct',
'hex2str',
'hostid',
'int2bit',
'int2char',
'int2enum',
'int2float',
'int2hex',
'int2oct',
'int2str',
'int2unichar',
'isbound',
'ischosen',
'ispresent',
'istemplatekind',
'isvalue',
'kill',
'killed',
'length',
'lengthof',
'log',
'map',
'match',
'oct2bit',
'oct2char',
'oct2hex',
'oct2int',
'oct2str',
'oct2unichar',
'raise',
'receive',
'record',
'regexp',
'remove_bom',
'replace',
'reply',
'rnd',
'running',
'send',
'setencode',
'setverdict',
'sizeof',
'start',
'stop',
'str2float',
'str2hex',
'str2int',
'str2oct',
'substr',
'testcasename',
'timeout',
'trigger',
'unichar2int',
'unichar2oct',
'unmap',
'value',
'valueof',
];
const LITERALS = [
'error',
'fail',
'false',
'inconc',
'infinity',
'none',
'null',
'omit',
'pass',
'true',
];
const TYPES = [
'address',
'anytype',
'bitstring',
'boolean',
'charstring',
'default',
'float',
'hexstring',
'integer',
'octetstring',
'universal',
'universal charstring',
'verdicttype',
];
const KEYWORDS = {
keyword: RESERVED_WORDS,
built_in: BUILT_INS,
literal: LITERALS,
type: TYPES,
};
const STRING = {
className: 'string',
variants: [
{ /* octstring */
match: /'([a-fA-F0-9]{2}|\?|\*)*'O/,
relevance: 10,
},
{ /* hexstring */
match: /'[a-fA-F0-9\?\*]*'H/,
relevance: 10,
},
{ /* bitstring */
match: /'[01\?\*]*'B/,
relevance: 10,
},
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
],
};
return {
name: 'TTCN-3',
aliases: [
'ttcn',
'ttcn3',
'ttcnpp',
],
keywords: KEYWORDS,
illegal: '(\\*=|\\+=|-=)',
contains: [
hljs.C_BLOCK_COMMENT_MODE,
hljs.C_LINE_COMMENT_MODE,
STRING,
]
};
}

View File

@ -0,0 +1,20 @@
import { describe, it } from "vitest";
import definitions from "./syntax_highlighting.js";
import hljs from "highlight.js";
describe("Syntax highlighting definitions", () => {
it("every entry is readable", async () => {
for (const [ mime, loader ] of Object.entries(definitions)) {
if (loader === null) {
continue;
}
const language = (await loader()).default;
hljs.registerLanguage(mime, language);
hljs.highlight("Hello world", {
language: mime
});
}
});
});

View File

@ -0,0 +1,178 @@
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
import type { LanguageFn } from "highlight.js";
type MimeRecord = Record<string, (() => Promise<{ default: LanguageFn}>) | null>;
const byMimeType: MimeRecord = {
"text/plain": () => import("highlight.js/lib/languages/plaintext"),
"application/dart": () => import("highlight.js/lib/languages/dart"),
"application/edn": () => import("highlight.js/lib/languages/clojure"),
"application/javascript;env=backend": () => import("highlight.js/lib/languages/javascript"),
"application/javascript;env=frontend": () => import("highlight.js/lib/languages/javascript"),
"application/json": () => import("highlight.js/lib/languages/json"),
"application/ld+json": () => import("highlight.js/lib/languages/json"),
"application/mbox": null,
"application/n-triples": null,
"application/pgp": null,
"application/sieve": null,
"application/sparql-query": null,
"application/typescript": () => import("highlight.js/lib/languages/typescript"),
"application/x-aspx": null,
"application/x-bat": () => import("highlight.js/lib/languages/dos"),
"application/x-cypher-query": () => import("highlightjs-cypher"),
"application/x-ejs": null,
"application/x-erb": () => import("highlight.js/lib/languages/erb"),
"application/x-jsp": () => import("highlight.js/lib/languages/java"),
"application/x-powershell": () => import("highlight.js/lib/languages/powershell"),
"application/xml-dtd": () => import("highlight.js/lib/languages/xml"),
"application/xquery": () => import("highlight.js/lib/languages/xquery"),
"message/http": () => import("highlight.js/lib/languages/http"),
"text/apl": null,
"text/coffeescript": () => import("highlight.js/lib/languages/coffeescript"),
"text/css": () => import("highlight.js/lib/languages/css"),
"text/html": () => import("highlight.js/lib/languages/xml"),
"text/jinja2": () => import("highlight.js/lib/languages/django"),
"text/jsx": () => import("highlight.js/lib/languages/javascript"),
"text/mirc": () => import("./languages/mirc.js"),
"text/tiki": null,
"text/troff": null,
"text/turtle": null,
"text/typescript-jsx": () => import("highlight.js/lib/languages/typescript"),
"text/vbscript": () => import("highlight.js/lib/languages/vbscript"),
"text/velocity": null,
"text/vnd.mermaid": null,
"text/mermaid": null,
"text/x-asm-mips": () => import("highlight.js/lib/languages/mipsasm"),
"text/x-asterisk": null,
"text/x-brainfuck": () => import("highlight.js/lib/languages/brainfuck"),
"text/x-c++src": () => import("highlight.js/lib/languages/cpp"),
"text/x-cassandra": null,
"text/x-clojure": () => import("highlight.js/lib/languages/clojure"),
"text/x-clojurescript": () => import("highlight.js/lib/languages/clojure"),
"text/x-cmake": () => import("highlight.js/lib/languages/cmake"),
"text/x-cobol": () => import("highlightjs-cobol"),
"text/x-common-lisp": () => import("highlight.js/lib/languages/lisp"),
"text/x-crystal": () => import("highlight.js/lib/languages/crystal"),
"text/x-csharp": () => import("highlight.js/lib/languages/csharp"),
"text/x-csrc": () => import("highlight.js/lib/languages/c"),
"text/x-cython": null,
"text/x-d": () => import("highlight.js/lib/languages/d"),
"text/x-diff": () => import("highlight.js/lib/languages/diff"),
"text/x-django": () => import("highlight.js/lib/languages/django"),
"text/x-dockerfile": () => import("highlight.js/lib/languages/dockerfile"),
"text/x-dylan": () => import("./languages/dylan.js"),
"text/x-ebnf": () => import("highlight.js/lib/languages/ebnf"),
"text/x-ecl": null,
"text/x-eiffel": null,
"text/x-elm": () => import("highlight.js/lib/languages/elm"),
"text/x-erlang": () => import("highlight.js/lib/languages/erlang"),
"text/x-esper": null,
"text/x-factor": null,
"text/x-fcl": null,
"text/x-feature": () => import("highlight.js/lib/languages/gherkin"),
"text/x-forth": null,
"text/x-fortran": () => import("highlight.js/lib/languages/fortran"),
"text/x-fsharp": () => import("highlight.js/lib/languages/fsharp"),
"text/x-gas": null,
"text/x-gdscript": () => import("@exercism/highlightjs-gdscript"),
"text/x-gfm": () => import("highlight.js/lib/languages/markdown"),
"text/x-go": () => import("highlight.js/lib/languages/go"),
"text/x-groovy": () => import("highlight.js/lib/languages/groovy"),
"text/x-gss": null,
"text/x-haml": () => import("highlight.js/lib/languages/haml"),
"text/x-haskell": () => import("highlight.js/lib/languages/haskell"),
"text/x-haxe": () => import("highlight.js/lib/languages/haxe"),
"text/x-hcl": () => import("./languages/terraform.js"),
"text/x-hxml": null,
"text/x-idl": null,
"text/x-java": () => import("highlight.js/lib/languages/java"),
"text/x-julia": () => import("highlight.js/lib/languages/julia"),
"text/x-kotlin": () => import("highlight.js/lib/languages/kotlin"),
"text/x-latex": () => import("highlight.js/lib/languages/latex"),
"text/x-less": () => import("highlight.js/lib/languages/less"),
"text/x-literate-haskell": null,
"text/x-livescript": () => import("highlight.js/lib/languages/livescript"),
"text/x-lua": () => import("highlight.js/lib/languages/lua"),
"text/x-mariadb": () => import("highlight.js/lib/languages/sql"),
"text/x-markdown": () => import("highlight.js/lib/languages/markdown"),
"text/x-mathematica": () => import("highlight.js/lib/languages/mathematica"),
"text/x-modelica": null,
"text/x-mscgen": null,
"text/x-msgenny": null,
"text/x-mssql": () => import("highlight.js/lib/languages/sql"),
"text/x-mumps": null,
"text/x-mysql": () => import("highlight.js/lib/languages/sql"),
"text/x-nix": () => import("highlight.js/lib/languages/nix"),
"text/x-nginx-conf": () => import("highlight.js/lib/languages/nginx"),
"text/x-nsis": () => import("highlight.js/lib/languages/nsis"),
"text/x-objectivec": () => import("highlight.js/lib/languages/objectivec"),
"text/x-ocaml": () => import("highlight.js/lib/languages/ocaml"),
"text/x-octave": null,
"text/x-oz": null,
"text/x-pascal": () => import("highlight.js/lib/languages/delphi"),
"text/x-perl": () => import("highlight.js/lib/languages/perl"),
"text/x-pgsql": () => import("highlight.js/lib/languages/sql"),
"text/x-php": () => import("highlight.js/lib/languages/php"),
"text/x-pig": null,
"text/x-plsql": () => import("highlight.js/lib/languages/sql"),
"text/x-properties": () => import("highlight.js/lib/languages/properties"),
"text/x-protobuf": () => import("highlight.js/lib/languages/protobuf"),
"text/x-pug": null,
"text/x-puppet": () => import("highlight.js/lib/languages/puppet"),
"text/x-python": () => import("highlight.js/lib/languages/python"),
"text/x-q": () => import("highlight.js/lib/languages/q"),
"text/x-rpm-changes": null,
"text/x-rpm-spec": () => import("./languages/rpm-specfile.js"),
"text/x-rsrc": () => import("highlight.js/lib/languages/r"),
"text/x-rst": null,
"text/x-ruby": () => import("highlight.js/lib/languages/ruby"),
"text/x-rustsrc": () => import("highlight.js/lib/languages/rust"),
"text/x-sas": () => import("highlight.js/lib/languages/sas"),
"text/x-sass": () => import("highlight.js/lib/languages/scss"),
"text/x-scala": () => import("highlight.js/lib/languages/scala"),
"text/x-scheme": () => import("highlight.js/lib/languages/scheme"),
"text/x-scss": () => import("highlight.js/lib/languages/scss"),
"text/x-sh": () => import("highlight.js/lib/languages/bash"),
"text/x-slim": null,
"text/x-smarty": null,
"text/x-sml": () => import("highlight.js/lib/languages/sml"),
"text/x-solr": null,
"text/x-soy": null,
"text/x-spreadsheet": null,
"text/x-sql": () => import("highlight.js/lib/languages/sql"),
"text/x-sqlite;schema=trilium": () => import("highlight.js/lib/languages/sql"),
"text/x-sqlite": () => import("highlight.js/lib/languages/sql"),
"text/x-squirrel": null,
"text/x-stex": null,
"text/x-stsrc": () => import("highlight.js/lib/languages/smalltalk"),
"text/x-styl": () => import("highlight.js/lib/languages/stylus"),
"text/x-swift": () => import("highlight.js/lib/languages/swift"),
"text/x-systemverilog": () => import("highlight.js/lib/languages/verilog"),
"text/x-tcl": () => import("highlight.js/lib/languages/tcl"),
"text/x-textile": null,
"text/x-tiddlywiki": null,
"text/x-toml": () => import("highlight.js/lib/languages/ini"),
"text/x-tornado": null,
"text/x-ttcn-asn": null,
"text/x-ttcn-cfg": null,
"text/x-ttcn": () => import("./languages/ttcn3.js"),
"text/x-twig": () => import("highlight.js/lib/languages/twig"),
"text/x-vb": () => import("highlight.js/lib/languages/vbnet"),
"text/x-verilog": () => import("highlight.js/lib/languages/verilog"),
"text/x-vhdl": () => import("highlight.js/lib/languages/vhdl"),
"text/x-vue": null,
"text/x-webidl": null,
"text/x-xu": null,
"text/x-yacas": null,
"text/x-yaml": () => import("highlight.js/lib/languages/yaml"),
"text/x-z80": null,
"text/xml": () => import("highlight.js/lib/languages/xml"),
}
const normalizedByMimeType: MimeRecord = {};
for (const [mimeType, loader] of Object.entries(byMimeType)) {
const normalizedMimeType = normalizeMimeTypeForCKEditor(mimeType);
normalizedByMimeType[normalizedMimeType] = loader;
}
export default normalizedByMimeType;

View File

@ -0,0 +1,10 @@
import themeDefinitions from "./themes.js";
import { describe, expect, it } from "vitest";
describe("Themes", () => {
it("all IDs don't contain spaces", () => {
for (const id of Object.keys(themeDefinitions)) {
expect(id).not.toMatch(/\s/);
}
});
});

View File

@ -0,0 +1,329 @@
export interface Theme {
name: string;
load: () => Promise<{ default: typeof import("*.css", { with: { "resolution-mode": "import" } }); }>;
}
const themeDefinitions: Record<string, Theme> = {
"1c-light": {
name: "1C (Light)",
load: () => import("../node_modules/highlight.js/styles/1c-light.css?raw")
},
"a11y-dark": {
name: "a11y (Dark)",
load: () => import("../node_modules/highlight.js/styles/a11y-dark.css?raw")
},
"a11y-light": {
name: "a11y (Light)",
load: () => import("../node_modules/highlight.js/styles/a11y-light.css?raw")
},
"agate": {
name: "Agate (Dark)",
load: () => import("../node_modules/highlight.js/styles/agate.css?raw")
},
"an-old-hope": {
name: "An Old Hope (Dark)",
load: () => import("../node_modules/highlight.js/styles/an-old-hope.css?raw")
},
"androidstudio": {
name: "Android Studio (Dark)",
load: () => import("../node_modules/highlight.js/styles/androidstudio.css?raw")
},
"arduino-light": {
name: "Arduino (Light)",
load: () => import("../node_modules/highlight.js/styles/arduino-light.css?raw")
},
"arta": {
name: "Arta (Dark)",
load: () => import("../node_modules/highlight.js/styles/arta.css?raw")
},
"ascetic": {
name: "Ascetic (Light)",
load: () => import("../node_modules/highlight.js/styles/ascetic.css?raw")
},
"atom-one-dark-reasonable": {
name: "Atom One with ReasonML support (Dark)",
load: () => import("../node_modules/highlight.js/styles/atom-one-dark-reasonable.css?raw")
},
"atom-one-dark": {
name: "Atom One (Dark)",
load: () => import("../node_modules/highlight.js/styles/atom-one-dark.css?raw")
},
"atom-one-light": {
name: "Atom One (Light)",
load: () => import("../node_modules/highlight.js/styles/atom-one-light.css?raw")
},
"brown-paper": {
name: "Brown Paper (Light)",
load: () => import("../node_modules/highlight.js/styles/brown-paper.css?raw")
},
"codepen-embed": {
name: "CodePen Embed (Dark)",
load: () => import("../node_modules/highlight.js/styles/codepen-embed.css?raw")
},
"color-brewer": {
name: "Color Brewer (Light)",
load: () => import("../node_modules/highlight.js/styles/color-brewer.css?raw")
},
"cybertopia-cherry": {
name: "Cybertopia Cherry (Dark)",
load: () => import("../node_modules/highlight.js/styles/cybertopia-cherry.css?raw")
},
"cybertopia-dimmer": {
name: "Cybertopia Dimmer (Dark)",
load: () => import("../node_modules/highlight.js/styles/cybertopia-dimmer.css?raw")
},
"cybertopia-icecap": {
name: "Cybertopia Icecap (Dark)",
load: () => import("../node_modules/highlight.js/styles/cybertopia-icecap.css?raw")
},
"cybertopia-saturated": {
name: "Cybertopia Saturated (Dark)",
load: () => import("../node_modules/highlight.js/styles/cybertopia-saturated.css?raw")
},
"dark": {
name: "Dark",
load: () => import("../node_modules/highlight.js/styles/dark.css?raw")
},
"default": {
name: "Original highlight.js Theme (Light)",
load: () => import("../node_modules/highlight.js/styles/default.css?raw")
},
"devibeans": {
name: "devibeans (Dark)",
load: () => import("../node_modules/highlight.js/styles/devibeans.css?raw")
},
"docco": {
name: "Docco (Light)",
load: () => import("../node_modules/highlight.js/styles/docco.css?raw")
},
"far": {
name: "FAR (Dark)",
load: () => import("../node_modules/highlight.js/styles/far.css?raw")
},
"felipec": {
name: "FelipeC (Dark)",
load: () => import("../node_modules/highlight.js/styles/felipec.css?raw")
},
"foundation": {
name: "Foundation 4 Docs (Light)",
load: () => import("../node_modules/highlight.js/styles/foundation.css?raw")
},
"github-dark-dimmed": {
name: "GitHub Dimmed (Dark)",
load: () => import("../node_modules/highlight.js/styles/github-dark-dimmed.css?raw")
},
"github-dark": {
name: "GitHub (Dark)",
load: () => import("../node_modules/highlight.js/styles/github-dark.css?raw")
},
"github": {
name: "GitHub (Light)",
load: () => import("../node_modules/highlight.js/styles/github.css?raw")
},
"gml": {
name: "GML (Dark)",
load: () => import("../node_modules/highlight.js/styles/gml.css?raw")
},
"googlecode": {
name: "Google Code (Light)",
load: () => import("../node_modules/highlight.js/styles/googlecode.css?raw")
},
"gradient-dark": {
name: "Gradient (Dark)",
load: () => import("../node_modules/highlight.js/styles/gradient-dark.css?raw")
},
"gradient-light": {
name: "Gradient (Light)",
load: () => import("../node_modules/highlight.js/styles/gradient-light.css?raw")
},
"grayscale": {
name: "Grayscale (Light)",
load: () => import("../node_modules/highlight.js/styles/grayscale.css?raw")
},
"hybrid": {
name: "hybrid (Dark)",
load: () => import("../node_modules/highlight.js/styles/hybrid.css?raw")
},
"idea": {
name: "Idea (Light)",
load: () => import("../node_modules/highlight.js/styles/idea.css?raw")
},
"intellij-light": {
name: "IntelliJ (Light)",
load: () => import("../node_modules/highlight.js/styles/intellij-light.css?raw")
},
"ir-black": {
name: "IR Black (Dark)",
load: () => import("../node_modules/highlight.js/styles/ir-black.css?raw")
},
"isbl-editor-dark": {
name: "ISBL Editor (Dark)",
load: () => import("../node_modules/highlight.js/styles/isbl-editor-dark.css?raw")
},
"isbl-editor-light": {
name: "ISBL Editor (Light)",
load: () => import("../node_modules/highlight.js/styles/isbl-editor-light.css?raw")
},
"kimbie-dark": {
name: "Kimbie (Dark)",
load: () => import("../node_modules/highlight.js/styles/kimbie-dark.css?raw")
},
"kimbie-light": {
name: "Kimbie (Light)",
load: () => import("../node_modules/highlight.js/styles/kimbie-light.css?raw")
},
"lightfair": {
name: "Lightfair (Light)",
load: () => import("../node_modules/highlight.js/styles/lightfair.css?raw")
},
"lioshi": {
name: "Lioshi (Dark)",
load: () => import("../node_modules/highlight.js/styles/lioshi.css?raw")
},
"magula": {
name: "Magula (Light)",
load: () => import("../node_modules/highlight.js/styles/magula.css?raw")
},
"mono-blue": {
name: "Mono Blue (Light)",
load: () => import("../node_modules/highlight.js/styles/mono-blue.css?raw")
},
"monokai-sublime": {
name: "Monokai Sublime (Dark)",
load: () => import("../node_modules/highlight.js/styles/monokai-sublime.css?raw")
},
"monokai": {
name: "Monokai (Dark)",
load: () => import("../node_modules/highlight.js/styles/monokai.css?raw")
},
"night-owl": {
name: "Night Owl (Dark)",
load: () => import("../node_modules/highlight.js/styles/night-owl.css?raw")
},
"nnfx-dark": {
name: "NNFX (Dark)",
load: () => import("../node_modules/highlight.js/styles/nnfx-dark.css?raw")
},
"nnfx-light": {
name: "NNFX (Light)",
load: () => import("../node_modules/highlight.js/styles/nnfx-light.css?raw")
},
"nord": {
name: "Nord (Dark)",
load: () => import("../node_modules/highlight.js/styles/nord.css?raw")
},
"obsidian": {
name: "Obsidian (Dark)",
load: () => import("../node_modules/highlight.js/styles/obsidian.css?raw")
},
"panda-syntax-dark": {
name: "Panda (Dark)",
load: () => import("../node_modules/highlight.js/styles/panda-syntax-dark.css?raw")
},
"panda-syntax-light": {
name: "Panda (Light)",
load: () => import("../node_modules/highlight.js/styles/panda-syntax-light.css?raw")
},
"paraiso-dark": {
name: "Paraiso (Dark)",
load: () => import("../node_modules/highlight.js/styles/paraiso-dark.css?raw")
},
"paraiso-light": {
name: "Paraiso (Light)",
load: () => import("../node_modules/highlight.js/styles/paraiso-light.css?raw")
},
"pojoaque": {
name: "Pojoaque (Dark)",
load: () => import("../node_modules/highlight.js/styles/pojoaque.css?raw")
},
"purebasic": {
name: "PureBasic (Light)",
load: () => import("../node_modules/highlight.js/styles/purebasic.css?raw")
},
"qtcreator-dark": {
name: "Qt Creator (Dark)",
load: () => import("../node_modules/highlight.js/styles/qtcreator-dark.css?raw")
},
"qtcreator-light": {
name: "Qt Creator (Light)",
load: () => import("../node_modules/highlight.js/styles/qtcreator-light.css?raw")
},
"rainbow": {
name: "Rainbow (Dark)",
load: () => import("../node_modules/highlight.js/styles/rainbow.css?raw")
},
"routeros": {
name: "RouterOS Script (Light)",
load: () => import("../node_modules/highlight.js/styles/routeros.css?raw")
},
"rose-pine-dawn": {
name: "Rose Pine Dawn (Light)",
load: () => import("../node_modules/highlight.js/styles/rose-pine-dawn.css?raw")
},
"rose-pine-moon": {
name: "Rose Pine Moon (Dark)",
load: () => import("../node_modules/highlight.js/styles/rose-pine-moon.css?raw")
},
"rose-pine": {
name: "Rose Pine (Dark)",
load: () => import("../node_modules/highlight.js/styles/rose-pine.css?raw")
},
"school-book": {
name: "School Book (Light)",
load: () => import("../node_modules/highlight.js/styles/school-book.css?raw")
},
"shades-of-purple": {
name: "Shades of Purple (Dark)",
load: () => import("../node_modules/highlight.js/styles/shades-of-purple.css?raw")
},
"srcery": {
name: "Srcery (Dark)",
load: () => import("../node_modules/highlight.js/styles/srcery.css?raw")
},
"stackoverflow-dark": {
name: "Stack Overflow (Dark)",
load: () => import("../node_modules/highlight.js/styles/stackoverflow-dark.css?raw")
},
"stackoverflow-light": {
name: "Stack Overflow (Light)",
load: () => import("../node_modules/highlight.js/styles/stackoverflow-light.css?raw")
},
"sunburst": {
name: "Sunburst (Dark)",
load: () => import("../node_modules/highlight.js/styles/sunburst.css?raw")
},
"tokyo-night-dark": {
name: "Tokyo Night (Dark)",
load: () => import("../node_modules/highlight.js/styles/tokyo-night-dark.css?raw")
},
"tokyo-night-light": {
name: "Tokyo Night (Light)",
load: () => import("../node_modules/highlight.js/styles/tokyo-night-light.css?raw")
},
"tomorrow-night-blue": {
name: "Tomorrow Night Blue (Dark)",
load: () => import("../node_modules/highlight.js/styles/tomorrow-night-blue.css?raw")
},
"tomorrow-night-bright": {
name: "Tomorrow Night Bright (Dark)",
load: () => import("../node_modules/highlight.js/styles/tomorrow-night-bright.css?raw")
},
"vs": {
name: "Visual Studio (Light)",
load: () => import("../node_modules/highlight.js/styles/vs.css?raw")
},
"vs2015": {
name: "Visual Studio 2015 (Dark)",
load: () => import("../node_modules/highlight.js/styles/vs2015.css?raw")
},
"xcode": {
name: "Xcode (Light)",
load: () => import("../node_modules/highlight.js/styles/xcode.css?raw")
},
"xt256": {
name: "xt256 (Dark)",
load: () => import("../node_modules/highlight.js/styles/xt256.css?raw")
}
}
export default themeDefinitions;

11
packages/highlightjs/src/types.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
declare module '@exercism/highlightjs-gdscript' {
import { LanguageFn } from "highlight.js";
const defineLanguage: LanguageFn;
export default defineLanguage;
}
declare module 'highlightjs-cypher' {
import { LanguageFn } from "highlight.js";
const defineLanguage: LanguageFn;
export default defineLanguage;
}

View File

@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "../commons"
},
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@ -0,0 +1,39 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"rootDir": "src",
"outDir": "dist",
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
"emitDeclarationOnly": true,
"forceConsistentCasingInFileNames": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"types": [
"node",
"vite/client"
]
},
"include": [
"src/**/*.ts"
],
"references": [
{
"path": "../commons/tsconfig.lib.json"
}
],
"exclude": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx"
]
}

View File

@ -0,0 +1,36 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out-tsc/vitest",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
],
"forceConsistentCasingInFileNames": true,
"noImplicitOverride": true,
"noImplicitReturns": true
},
"include": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}

View File

@ -0,0 +1,42 @@
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
import * as path from 'path';
export default defineConfig(() => ({
root: __dirname,
cacheDir: '../../node_modules/.vite/packages/highlightjs',
plugins: [dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') }),],
build: {
outDir: './dist',
emptyOutDir: true,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
lib: {
// Could also be a dictionary or array of multiple entry points.
entry: 'src/index.ts',
name: 'highlightjs',
fileName: 'index',
// Change this to the formats you want to support.
// Don't forget to update your package.json as well.
formats: ['es' as const]
},
rollupOptions: {
// External packages that should not be bundled into your library.
external: []
},
},
test: {
'watch': false,
'globals': true,
'environment': "happy-dom",
'include': ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
'reporters': ["default"],
'coverage': {
'reportsDirectory': './test-output/vitest/coverage',
'provider': 'v8' as const,
}
},
}));

1156
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -53,6 +53,9 @@
},
{
"path": "./packages/codemirror"
},
{
"path": "./packages/highlightjs"
}
]
}