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
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) [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: 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> <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? ## ⚠️ 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? ### 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. 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. 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) - [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) - [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) - [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 ## 💬 Discuss with us
Feel free to join our official conversations. We would love to hear what features, suggestions, or issues you may have! 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) - 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) - [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) - [Github Issues](https://github.com/TriliumNext/Notes/issues) (For bug reports and feature requests.)
## 🎁 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.
## 🏗 Installation ## 🏗 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. ### Linux
* Access TriliumNext via the web interface of a server installation (see below)
* Currently only the latest versions of Chrome & Firefox are supported (and tested). If your distribution is listed in the table below, use your distribution's package.
* TriliumNext is also provided as a Flatpak, but not yet published on FlatHub.
[![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 ### 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). 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 ## 💻 Contribute
@@ -150,4 +162,6 @@ Support for the TriliumNext organization will be possible in the near future. Fo
## 🔑 License ## 🔑 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. 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", "@playwright/test": "1.52.0",
"@stylistic/eslint-plugin": "4.2.0", "@stylistic/eslint-plugin": "4.2.0",
"@types/express": "5.0.1", "@types/express": "5.0.1",
"@types/node": "22.15.18", "@types/node": "22.15.19",
"@types/yargs": "17.0.33", "@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.1.3", "@vitest/coverage-v8": "3.1.4",
"eslint": "9.27.0", "eslint": "9.27.0",
"eslint-plugin-simple-import-sort": "12.1.1", "eslint-plugin-simple-import-sort": "12.1.1",
"esm": "3.2.25", "esm": "3.2.25",

View File

@@ -31,7 +31,7 @@
"draggabilly": "3.0.0", "draggabilly": "3.0.0",
"force-graph": "1.49.6", "force-graph": "1.49.6",
"globals": "16.1.0", "globals": "16.1.0",
"i18next": "25.1.3", "i18next": "25.2.0",
"i18next-http-backend": "3.0.2", "i18next-http-backend": "3.0.2",
"jquery": "3.7.1", "jquery": "3.7.1",
"jquery-hotkeys": "0.2.2", "jquery-hotkeys": "0.2.2",
@@ -41,7 +41,7 @@
"leaflet": "1.9.4", "leaflet": "1.9.4",
"leaflet-gpx": "2.2.0", "leaflet-gpx": "2.2.0",
"mark.js": "8.11.1", "mark.js": "8.11.1",
"marked": "15.0.11", "marked": "15.0.12",
"mermaid": "11.6.0", "mermaid": "11.6.0",
"mind-elixir": "4.5.2", "mind-elixir": "4.5.2",
"panzoom": "9.4.3", "panzoom": "9.4.3",
@@ -55,7 +55,7 @@
"@ckeditor/ckeditor5-inspector": "4.1.0", "@ckeditor/ckeditor5-inspector": "4.1.0",
"@types/bootstrap": "5.2.10", "@types/bootstrap": "5.2.10",
"@types/jquery": "3.5.32", "@types/jquery": "3.5.32",
"@types/leaflet": "1.9.17", "@types/leaflet": "1.9.18",
"@types/leaflet-gpx": "1.3.7", "@types/leaflet-gpx": "1.3.7",
"@types/react": "19.1.4", "@types/react": "19.1.4",
"@types/react-dom": "19.1.5", "@types/react-dom": "19.1.5",

View File

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

View File

@@ -78,15 +78,15 @@ export default class RootCommandExecutor extends Component {
} }
hideLeftPaneCommand() { hideLeftPaneCommand() {
options.save(`leftPaneVisible`, "false"); appContext.triggerEvent("setLeftPaneVisibility", { leftPaneVisible: false });
} }
showLeftPaneCommand() { showLeftPaneCommand() {
options.save(`leftPaneVisible`, "true"); appContext.triggerEvent("setLeftPaneVisibility", { leftPaneVisible: true });
} }
toggleLeftPaneCommand() { toggleLeftPaneCommand() {
options.toggle("leftPaneVisible"); appContext.triggerEvent("setLeftPaneVisibility", { leftPaneVisible: null });
} }
async showBackendLogCommand() { 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 imageContextMenuService from "../menus/image_context_menu.js";
import { applySingleBlockSyntaxHighlight, applySyntaxHighlight } from "./syntax_highlight.js"; import { applySingleBlockSyntaxHighlight, applySyntaxHighlight } from "./syntax_highlight.js";
import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js"; import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js";
import { normalizeMimeTypeForCKEditor } from "./mime_type_definitions.js";
import renderDoc from "./doc_renderer.js"; import renderDoc from "./doc_renderer.js";
import { t } from "../services/i18n.js"; import { t } from "../services/i18n.js";
import WheelZoom from 'vanilla-js-wheel-zoom'; import WheelZoom from 'vanilla-js-wheel-zoom';
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
let idCounter = 1; 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 { export interface Library {
js?: string[] | (() => string[]); js?: string[] | (() => string[]);
css?: string[]; css?: string[];
@@ -12,32 +8,6 @@ const KATEX: Library = {
css: ["node_modules/katex/dist/katex.min.css"] 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) { async function requireLibrary(library: Library) {
if (library.css) { if (library.css) {
library.css.map((cssUrl) => requireCss(cssUrl)); 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 { export default {
requireCss, requireCss,
requireLibrary, requireLibrary,
loadHighlightingTheme, KATEX
KATEX,
HIGHLIGHT_JS
}; };

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"; 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; let mimeTypes: MimeType[] | null = null;
function loadMimeTypes() { function loadMimeTypes() {
@@ -45,8 +38,8 @@ export function getHighlightJsNameForMime(mimeType: string) {
for (const mimeType of mimeTypes) { 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. // 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); const normalizedMime = normalizeMimeTypeForCKEditor(mimeType.mime);
if (mimeType.highlightJs) { if (mimeType.mdLanguageCode) {
mimeToHighlightJsMapping[normalizedMime] = mimeType.highlightJs; mimeToHighlightJsMapping[normalizedMime] = mimeType.mdLanguageCode;
} }
} }
} }

View File

@@ -3,7 +3,11 @@ import Split from "split.js"
export const DEFAULT_GUTTER_SIZE = 5; export const DEFAULT_GUTTER_SIZE = 5;
let leftPaneWidth: number;
let reservedPx: number;
let layoutOrientation: string;
let leftInstance: ReturnType<typeof Split> | null; let leftInstance: ReturnType<typeof Split> | null;
let rightPaneWidth: number;
let rightInstance: ReturnType<typeof Split> | null; let rightInstance: ReturnType<typeof Split> | null;
function setupLeftPaneResizer(leftPaneVisible: boolean) { function setupLeftPaneResizer(leftPaneVisible: boolean) {
@@ -14,27 +18,34 @@ function setupLeftPaneResizer(leftPaneVisible: boolean) {
$("#left-pane").toggle(leftPaneVisible); $("#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) { if (!leftPaneVisible) {
$("#rest-pane").css("width", "100%"); $("#rest-pane").css("width", layoutOrientation === "vertical" ? `${100 - reservedWidth}%` : "100%");
return; return;
} }
let leftPaneWidth = options.getInt("leftPaneWidth"); leftPaneWidth = leftPaneWidth ?? (options.getInt("leftPaneWidth") ?? 0);
if (!leftPaneWidth || leftPaneWidth < 5) { if (!leftPaneWidth || leftPaneWidth < 5) {
leftPaneWidth = 5; leftPaneWidth = 5;
} }
const restPaneWidth = 100 - leftPaneWidth - reservedWidth;
if (leftPaneVisible) { if (leftPaneVisible) {
// Delayed initialization ensures that all DOM elements are fully rendered and part of the layout, // 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, // 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. // which would cause the minSize setting to have no effect.
requestAnimationFrame(() => { requestAnimationFrame(() => {
leftInstance = Split(["#left-pane", "#rest-pane"], { leftInstance = Split(["#left-pane", "#rest-pane"], {
sizes: [leftPaneWidth, 100 - leftPaneWidth], sizes: [leftPaneWidth, restPaneWidth],
gutterSize: DEFAULT_GUTTER_SIZE, gutterSize: DEFAULT_GUTTER_SIZE,
minSize: [150, 300], 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; return;
} }
let rightPaneWidth = options.getInt("rightPaneWidth"); rightPaneWidth = rightPaneWidth ?? (options.getInt("rightPaneWidth") ?? 0);
if (!rightPaneWidth || rightPaneWidth < 5) { if (!rightPaneWidth || rightPaneWidth < 5) {
rightPaneWidth = 5; rightPaneWidth = 5;
} }
@@ -63,8 +74,11 @@ function setupRightPaneResizer() {
rightInstance = Split(["#center-pane", "#right-pane"], { rightInstance = Split(["#center-pane", "#right-pane"], {
sizes: [100 - rightPaneWidth, rightPaneWidth], sizes: [100 - rightPaneWidth, rightPaneWidth],
gutterSize: DEFAULT_GUTTER_SIZE, gutterSize: DEFAULT_GUTTER_SIZE,
minSize: [ 300, 180 ], minSize: [300, 180],
onDragEnd: (sizes) => options.save("rightPaneWidth", Math.round(sizes[1])) 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 mime_types from "./mime_types.js";
import options from "./options.js"; import options from "./options.js";
export function getStylesheetUrl(theme: string) { let highlightingLoaded = false;
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;
}
/** /**
* 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. * 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; return;
} }
await ensureMimeTypesForHighlighting();
const codeBlocks = $container.find("pre code"); const codeBlocks = $container.find("pre code");
for (const codeBlock of codeBlocks) { for (const codeBlock of codeBlocks) {
const normalizedMimeType = extractLanguageFromClassList(codeBlock); const normalizedMimeType = extractLanguageFromClassList(codeBlock);
@@ -43,20 +34,13 @@ export async function applySingleBlockSyntaxHighlight($codeBlock: JQuery<HTMLEle
$codeBlock.parent().toggleClass("hljs"); $codeBlock.parent().toggleClass("hljs");
const text = $codeBlock.text(); const text = $codeBlock.text();
if (!window.hljs) {
await library_loader.requireLibrary(library_loader.HIGHLIGHT_JS);
}
let highlightedText = null; let highlightedText = null;
if (normalizedMimeType === mime_types.MIME_TYPE_AUTO) { if (normalizedMimeType === mime_types.MIME_TYPE_AUTO) {
highlightedText = hljs.highlightAuto(text); await ensureMimeTypesForHighlighting();
highlightedText = highlightAuto(text);
} else if (normalizedMimeType) { } else if (normalizedMimeType) {
const language = mime_types.getHighlightJsNameForMime(normalizedMimeType); await ensureMimeTypesForHighlighting();
if (language) { highlightedText = highlight(text, { language: normalizedMimeType });
highlightedText = hljs.highlight(text, { language });
} else {
console.warn(`Unknown mime type: ${normalizedMimeType}.`);
}
} }
if (highlightedText) { 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. * 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. * @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); --launcher-pane-button-gap: var(--launcher-pane-vert-button-gap);
width: var(--launcher-pane-size) !important; width: var(--launcher-pane-size) !important;
min-width: var(--launcher-pane-size) !important;
padding-bottom: var(--launcher-pane-button-gap); padding-bottom: var(--launcher-pane-button-gap);
} }

View File

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

View File

@@ -1573,7 +1573,10 @@
"color-scheme": "Farbschema" "color-scheme": "Farbschema"
}, },
"code_block": { "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": { "classic_editor_toolbar": {
"title": "Format" "title": "Format"

View File

@@ -1827,7 +1827,10 @@
"color-scheme": "Color Scheme" "color-scheme": "Color Scheme"
}, },
"code_block": { "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": { "classic_editor_toolbar": {
"title": "Formatting" "title": "Formatting"

View File

@@ -1589,7 +1589,10 @@
"color-scheme": "Esquema de color" "color-scheme": "Esquema de color"
}, },
"code_block": { "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": { "classic_editor_toolbar": {
"title": "Formato" "title": "Formato"

View File

@@ -1579,7 +1579,10 @@
"color-scheme": "Jeu de couleurs" "color-scheme": "Jeu de couleurs"
}, },
"code_block": { "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": { "classic_editor_toolbar": {
"title": "Mise en forme" "title": "Mise en forme"

View File

@@ -1,5 +1,10 @@
{ {
"revisions": { "revisions": {
"delete_button": "" "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." "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": { "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": { "classic_editor_toolbar": {
"title": "Formatare" "title": "Formatare"

View File

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

View File

@@ -124,13 +124,6 @@ declare global {
var __non_webpack_require__: RequireMethod | undefined; var __non_webpack_require__: RequireMethod | undefined;
// Libraries // 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: { var renderMathInElement: (element: HTMLElement, options: {
trust: boolean; trust: boolean;
}) => void; }) => void;

View File

@@ -53,10 +53,6 @@ const TPL = /*html*/`
pointer-events: none; pointer-events: none;
} }
.update-to-latest-version-button {
display: none;
}
.global-menu .zoom-container { .global-menu .zoom-container {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@@ -235,7 +231,7 @@ const TPL = /*html*/`
${t("global_menu.about")} ${t("global_menu.about")}
</li> </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="bx bx-sync"></span>
<span class="version-text"></span> <span class="version-text"></span>

View File

@@ -19,10 +19,10 @@ export default class LeftPaneToggleWidget extends CommandButtonWidget {
return "bx-sidebar"; 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"); this.settings.command = () => (this.currentLeftPaneVisible ? "hideLeftPane" : "showLeftPane");
@@ -32,16 +32,12 @@ export default class LeftPaneToggleWidget extends CommandButtonWidget {
} }
refreshIcon() { refreshIcon() {
if (document.hasFocus() || this.currentLeftPaneVisible === true) { super.refreshIcon();
super.refreshIcon(); splitService.setupLeftPaneResizer(this.currentLeftPaneVisible);
splitService.setupLeftPaneResizer(this.currentLeftPaneVisible);
}
} }
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { setLeftPaneVisibilityEvent({ leftPaneVisible }: EventData<"setLeftPaneVisibility">) {
if (loadResults.isOptionReloaded("leftPaneVisible") && document.hasFocus()) { this.currentLeftPaneVisible = leftPaneVisible ?? !this.currentLeftPaneVisible;
this.currentLeftPaneVisible = options.is("leftPaneVisible"); this.refreshIcon();
this.refreshIcon();
}
} }
} }

View File

@@ -4,28 +4,33 @@ import appContext, { type EventData } from "../../components/app_context.js";
import type Component from "../../components/component.js"; import type Component from "../../components/component.js";
export default class LeftPaneContainer extends FlexContainer<Component> { export default class LeftPaneContainer extends FlexContainer<Component> {
private currentLeftPaneVisible: boolean;
constructor() { constructor() {
super("column"); super("column");
this.currentLeftPaneVisible = options.is("leftPaneVisible");
this.id("left-pane"); this.id("left-pane");
this.css("height", "100%"); this.css("height", "100%");
this.collapsible(); this.collapsible();
} }
isEnabled() { isEnabled() {
return super.isEnabled() && options.is("leftPaneVisible"); return super.isEnabled() && this.currentLeftPaneVisible;
} }
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { setLeftPaneVisibilityEvent({ leftPaneVisible }: EventData<"setLeftPaneVisibility">) {
if (loadResults.isOptionReloaded("leftPaneVisible") && document.hasFocus()) { this.currentLeftPaneVisible = leftPaneVisible ?? !this.currentLeftPaneVisible;
const visible = this.isEnabled(); const visible = this.isEnabled();
this.toggleInt(visible); this.toggleInt(visible);
if (visible) { if (visible) {
this.triggerEvent("focusTree", {}); this.triggerEvent("focusTree", {});
} else { } else {
this.triggerEvent("focusOnDetail", { ntxId: appContext.tabManager.getActiveContext()?.ntxId }); 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 protectedSessionHolder from "../services/protected_session_holder.js";
import SpacedUpdate from "../services/spaced_update.js"; import SpacedUpdate from "../services/spaced_update.js";
import server from "../services/server.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 appContext, { type CommandListenerData, type EventData } from "../components/app_context.js";
import keyboardActionsService from "../services/keyboard_actions.js"; import keyboardActionsService from "../services/keyboard_actions.js";
import noteCreateService from "../services/note_create.js"; import noteCreateService from "../services/note_create.js";
import attributeService from "../services/attributes.js"; import attributeService from "../services/attributes.js";
import attributeRenderer from "../services/attribute_renderer.js";
import EmptyTypeWidget from "./type_widgets/empty.js"; import EmptyTypeWidget from "./type_widgets/empty.js";
import EditableTextTypeWidget from "./type_widgets/editable_text.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 AttachmentListTypeWidget from "./type_widgets/attachment_list.js";
import AttachmentDetailTypeWidget from "./type_widgets/attachment_detail.js"; import AttachmentDetailTypeWidget from "./type_widgets/attachment_detail.js";
import MindMapWidget from "./type_widgets/mind_map.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 GeoMapTypeWidget from "./type_widgets/geo_map.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import type { NoteType } from "../entities/fnote.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 { 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 { getHighlightJsNameForMime } from "../../../services/mime_types.js";
import options from "../../../services/options.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 utils from "../../../services/utils.js";
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?external"; import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?external";
@@ -104,9 +103,9 @@ export function buildConfig() {
definitionsUrl: emojiDefinitionsUrl definitionsUrl: emojiDefinitionsUrl
}, },
syntaxHighlighting: { syntaxHighlighting: {
async loadHighlightJs() { loadHighlightJs: async () => {
await library_loader.requireLibrary(library_loader.HIGHLIGHT_JS); await ensureMimeTypesForHighlighting();
return hljs; return await import("@triliumnext/highlightjs");
}, },
mapLanguageName: getHighlightJsNameForMime, mapLanguageName: getHighlightJsNameForMime,
defaultMimeType: MIME_TYPE_AUTO, 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 dialogService from "../../services/dialog.js";
import options from "../../services/options.js"; import options from "../../services/options.js";
import toast from "../../services/toast.js"; import toast from "../../services/toast.js";
import { normalizeMimeTypeForCKEditor } from "../../services/mime_type_definitions.js";
import { buildSelectedBackgroundColor } from "../../components/touch_bar.js"; import { buildSelectedBackgroundColor } from "../../components/touch_bar.js";
import { buildConfig, buildToolbarConfig } from "./ckeditor/config.js"; import { buildConfig, buildToolbarConfig } from "./ckeditor/config.js";
import type FNote from "../../entities/fnote.js"; import type FNote from "../../entities/fnote.js";
import { getMermaidConfig } from "../../services/mermaid.js"; import { getMermaidConfig } from "../../services/mermaid.js";
import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig } from "@triliumnext/ckeditor5"; import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig } from "@triliumnext/ckeditor5";
import "@triliumnext/ckeditor5/index.css"; import "@triliumnext/ckeditor5/index.css";
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
const ENABLE_INSPECTOR = false; 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 { t } from "../../../../services/i18n.js";
import library_loader from "../../../../services/library_loader.js";
import server from "../../../../services/server.js"; import server from "../../../../services/server.js";
import OptionsWidget from "../options_widget.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 SAMPLE_CODE = `\
const n = 10; const n = 10;
greet(n); // Print "Hello World" for n times greet(n); // Print "Hello World" for n times
@@ -55,14 +56,6 @@ const TPL = /*html*/`
</div> </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. * 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() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$themeSelect = this.$widget.find(".theme-select"); 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 () => { this.$themeSelect.on("change", async () => {
const newTheme = String(this.$themeSelect.val()); const newTheme = String(this.$themeSelect.val());
library_loader.loadHighlightingTheme(newTheme); loadHighlightingTheme(newTheme);
await server.put(`options/codeBlockTheme/${newTheme}`); await server.put(`options/codeBlockTheme/${newTheme}`);
}); });
@@ -91,11 +106,14 @@ export default class CodeBlockOptions extends OptionsWidget {
#setupPreview(shouldEnableSyntaxHighlight: boolean) { #setupPreview(shouldEnableSyntaxHighlight: boolean) {
const text = SAMPLE_CODE; const text = SAMPLE_CODE;
if (shouldEnableSyntaxHighlight) { if (shouldEnableSyntaxHighlight) {
library_loader.requireLibrary(library_loader.HIGHLIGHT_JS).then(() => { import("@triliumnext/highlightjs").then(async (hljs) => {
await ensureMimeTypesForHighlighting();
const highlightedText = hljs.highlight(text, { const highlightedText = hljs.highlight(text, {
language: SAMPLE_LANGUAGE language: SAMPLE_LANGUAGE
}); });
this.$sampleEl.html(highlightedText.value); if (highlightedText) {
this.$sampleEl.html(highlightedText.value);
}
}); });
} else { } else {
this.$sampleEl.text(text); this.$sampleEl.text(text);
@@ -103,25 +121,6 @@ export default class CodeBlockOptions extends OptionsWidget {
} }
async optionsLoaded(options: OptionMap) { 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.$themeSelect.val(options.codeBlockTheme);
this.setCheckboxState(this.$wordWrap, options.codeBlockWordWrap); this.setCheckboxState(this.$wordWrap, options.codeBlockWordWrap);
this.$widget.closest(".note-detail-printable").toggleClass("word-wrap", options.codeBlockWordWrap === "true"); 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"); 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" "src/**/*.ts"
], ],
"references": [ "references": [
{
"path": "../../packages/highlightjs/tsconfig.lib.json"
},
{ {
"path": "../../packages/codemirror/tsconfig.lib.json" "path": "../../packages/codemirror/tsconfig.lib.json"
}, },

View File

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

View File

@@ -80,6 +80,7 @@ module.exports = composePlugins(
})) }))
})); }));
inlineCss(config);
inlineSvg(config); inlineSvg(config);
externalJson(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) { function externalJson(config) {
if (!config.module?.rules) { if (!config.module?.rules) {
return; 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-dl": "4.0.0",
"electron-squirrel-startup": "1.0.1", "electron-squirrel-startup": "1.0.1",
"jquery.fancytree": "2.38.5", "jquery.fancytree": "2.38.5",
"jquery-hotkeys": "0.2.2", "jquery-hotkeys": "0.2.2"
"@highlightjs/cdn-assets": "11.11.1"
}, },
"devDependencies": { "devDependencies": {
"@types/electron-squirrel-startup": "1.0.2", "@types/electron-squirrel-startup": "1.0.2",

View File

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

View File

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

View File

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

View File

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

View File

@@ -189,11 +189,6 @@
"special_notes": { "special_notes": {
"search_prefix": "Buscar:" "search_prefix": "Buscar:"
}, },
"code_block": {
"theme_none": "Sin resaltado de sintaxis",
"theme_group_light": "Temas claros",
"theme_group_dark": "Temas oscuros"
},
"test_sync": { "test_sync": {
"not-configured": "El servidor de sincronización no está configurado. Por favor configure primero la sincronización.", "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." "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": { "special_notes": {
"search_prefix": "Recherche :" "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": { "test_sync": {
"not-configured": "L'hôte du serveur de synchronisation n'est pas configuré. Veuillez d'abord configurer la synchronisation.", "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." "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": { "special_notes": {
"search_prefix": "Pesquisar:" "search_prefix": "Pesquisar:"
}, },
"code_block": {
"theme_none": "Sem destaque de sintaxe",
"theme_group_light": "Temas claros",
"theme_group_dark": "Temas escuros"
},
"test_sync": { "test_sync": {
"not-configured": "O host do servidor de sincronização não está configurado. Por favor, configure a sincronização primeiro.", "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." "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": { "special_notes": {
"search_prefix": "Căutare:" "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": { "test_sync": {
"not-configured": "Calea către serverul de sincronizare nu este configurată. Configurați sincronizarea înainte.", "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." "successful": "Comunicarea cu serverul de sincronizare a avut loc cu succes, s-a început sincronizarea."

View File

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

View File

@@ -6,7 +6,6 @@ import searchService from "../../services/search/services/search.js";
import ValidationError from "../../errors/validation_error.js"; import ValidationError from "../../errors/validation_error.js";
import type { Request } from "express"; import type { Request } from "express";
import { changeLanguage, getLocales } from "../../services/i18n.js"; import { changeLanguage, getLocales } from "../../services/i18n.js";
import { listSyntaxHighlightingThemes } from "../../services/code_block_theme.js";
import type { OptionNames } from "@triliumnext/commons"; import type { OptionNames } from "@triliumnext/commons";
// options allowed to be updated directly in the Options dialog // options allowed to be updated directly in the Options dialog
@@ -191,10 +190,6 @@ function getUserThemes() {
return ret; return ret;
} }
function getSyntaxHighlightingThemes() {
return listSyntaxHighlightingThemes();
}
function getSupportedLocales() { function getSupportedLocales() {
return getLocales(); return getLocales();
} }
@@ -211,6 +206,5 @@ export default {
updateOption, updateOption,
updateOptions, updateOptions,
getUserThemes, getUserThemes,
getSyntaxHighlightingThemes,
getSupportedLocales 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/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/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 { 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/:name/:value*", optionsApiRoute.updateOption);
apiRoute(PUT, "/api/options", optionsApiRoute.updateOptions); apiRoute(PUT, "/api/options", optionsApiRoute.updateOptions);
apiRoute(GET, "/api/options/user-themes", optionsApiRoute.getUserThemes); apiRoute(GET, "/api/options/user-themes", optionsApiRoute.getUserThemes);
apiRoute(GET, "/api/options/codeblock-themes", optionsApiRoute.getSyntaxHighlightingThemes);
apiRoute(GET, "/api/options/locales", optionsApiRoute.getSupportedLocales); apiRoute(GET, "/api/options/locales", optionsApiRoute.getSupportedLocales);
apiRoute(PST, "/api/password/change", passwordApiRoute.changePassword); 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"; "use strict";
import { parse, Renderer, type Tokens } from "marked"; 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. * 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) { function renderToHtml(content: string, title: string) {
// Double-escape slashes in math expression because they are otherwise consumed by the parser somewhere. // Double-escape slashes in math expression because they are otherwise consumed by the parser somewhere.
content = content.replaceAll("\\$", "\\\\$"); content = content.replaceAll("\\$", "\\\\$");
@@ -158,15 +156,17 @@ function renderToHtml(content: string, title: string) {
function getNormalizedMimeFromMarkdownLanguage(language: string | undefined) { function getNormalizedMimeFromMarkdownLanguage(language: string | undefined) {
if (language) { if (language) {
const highlightJsName = getMimeTypeFromHighlightJs(language); const mimeDefinition = getMimeTypeFromMarkdownName(language);
if (highlightJsName) { if (mimeDefinition) {
return normalizeMimeTypeForCKEditor(highlightJsName.mime); return normalizeMimeTypeForCKEditor(mimeDefinition.mime);
} }
} }
return MIME_TYPE_AUTO; return MIME_TYPE_AUTO;
} }
const renderer = new CustomMarkdownRenderer({ async: false });
export default { export default {
renderToHtml 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", "autocomplete.js/dist",
"normalize.css/normalize.css", "normalize.css/normalize.css",
"jquery.fancytree/dist", "jquery.fancytree/dist",
"@highlightjs/cdn-assets",
// Required as they are native dependencies and cannot be well bundled. // Required as they are native dependencies and cannot be well bundled.
"better-sqlite3", "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 * [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 * [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) * [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 ## ✨ 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 * [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 * [Only expand/collapse the left pane of the focused window](https://github.com/TriliumNext/Notes/pull/1905) by @SiriusXT
* Code notes: * 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 the Nix language (and also in code blocks for text notes).
* Added an indentation marker. * 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. * 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. * 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. * 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 * [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. * Documented the new text note features: bookmarks and emojis.
* Add documentation links and updated pnpm commands to README by @perfectra1n * 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 * Add documentation around setting the various environment variables to control upload size limit by @perfectra1n
* README improvements by @FliegendeWurst
## 🌍 Internationalization ## 🌍 Internationalization

View File

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

View File

@@ -173,14 +173,6 @@ export default class SyntaxHighlighting extends Plugin {
return; 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. // Don't highlight if the code is too big, as the typing performance will be highly degraded.
if (codeBlock.childCount >= HIGHLIGHT_MAX_BLOCK_COUNT) { if (codeBlock.childCount >= HIGHLIGHT_MAX_BLOCK_COUNT) {
return; return;
@@ -230,8 +222,13 @@ export default class SyntaxHighlighting extends Plugin {
if (mimeType === this.config.defaultMimeType) { if (mimeType === this.config.defaultMimeType) {
highlightRes = this.hljs.highlightAuto(text); highlightRes = this.hljs.highlightAuto(text);
} else { } else {
highlightRes = this.hljs.highlight(text, { language: highlightJsLanguage }); highlightRes = this.hljs.highlight(text, { language: mimeType });
} }
if (!highlightRes) {
return;
}
dbg("text\n" + text); dbg("text\n" + text);
dbg("html\n" + highlightRes.value); 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/keyboard_actions_interface.js";
export * from "./lib/hidden_subtree.js"; export * from "./lib/hidden_subtree.js";
export * from "./lib/rows.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'; import { defineConfig } from 'vite';
export default defineConfig(() => ({ export default defineConfig(() => ({
root: __dirname, root: __dirname,
cacheDir: '../../node_modules/.vite/packages/commons', cacheDir: '../../node_modules/.vite/packages/commons',
plugins: [], plugins: [],
// Uncomment this if you are using workers. test: {
// worker: { 'watch': false,
// plugins: [ nxViteTsPaths() ], 'globals': true,
// }, 'environment': "node",
test: { 'include': ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
'watch': false, 'reporters': ["default"],
'globals': true, 'coverage': {
'environment': "node", 'reportsDirectory': './test-output/vitest/coverage',
'include': ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], 'provider': 'v8' as const,
'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/codemirror"
},
{
"path": "./packages/highlightjs"
} }
] ]
} }