mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 19:19:03 +01:00 
			
		
		
		
	Merge branch 'develop' into math-edit
This commit is contained in:
		
						commit
						5fcf4afcfa
					
				| @ -84,7 +84,7 @@ Download the binary release for your platform from the [latest release page](htt | |||||||
| 
 | 
 | ||||||
| If your distribution is listed in the table below, use your distribution's package. | If your distribution is listed in the table below, use your distribution's package. | ||||||
| 
 | 
 | ||||||
| [](https://repology.org/project/trilium-next-desktop/versions) | [](https://repology.org/project/triliumnext/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. | 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. | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ import treeService from "./tree.js"; | |||||||
| import FNote from "../entities/fnote.js"; | import FNote from "../entities/fnote.js"; | ||||||
| import FAttachment from "../entities/fattachment.js"; | 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, formatCodeBlocks } from "./syntax_highlight.js"; | ||||||
| import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js"; | import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.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"; | ||||||
| @ -106,7 +106,7 @@ async function renderText(note: FNote | FAttachment, $renderedContent: JQuery<HT | |||||||
|             await linkService.loadReferenceLinkTitle($(el)); |             await linkService.loadReferenceLinkTitle($(el)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         await applySyntaxHighlight($renderedContent); |         await formatCodeBlocks($renderedContent); | ||||||
|     } else if (note instanceof FNote) { |     } else if (note instanceof FNote) { | ||||||
|         await renderChildrenList($renderedContent, note); |         await renderChildrenList($renderedContent, note); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import type FNote from "../entities/fnote.js"; | import type FNote from "../entities/fnote.js"; | ||||||
| import { getCurrentLanguage } from "./i18n.js"; | import { getCurrentLanguage } from "./i18n.js"; | ||||||
| import { applySyntaxHighlight } from "./syntax_highlight.js"; | import { formatCodeBlocks } from "./syntax_highlight.js"; | ||||||
| 
 | 
 | ||||||
| export default function renderDoc(note: FNote) { | export default function renderDoc(note: FNote) { | ||||||
|     return new Promise<JQuery<HTMLElement>>((resolve) => { |     return new Promise<JQuery<HTMLElement>>((resolve) => { | ||||||
| @ -41,7 +41,7 @@ function processContent(url: string, $content: JQuery<HTMLElement>) { | |||||||
|         $img.attr("src", dir + "/" + $img.attr("src")); |         $img.attr("src", dir + "/" + $img.attr("src")); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     applySyntaxHighlight($content); |     formatCodeBlocks($content); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getUrl(docNameValue: string, language: string) { | function getUrl(docNameValue: string, language: string) { | ||||||
|  | |||||||
| @ -1,20 +1,22 @@ | |||||||
| import { ensureMimeTypes, highlight, highlightAuto, loadTheme, Themes } from "@triliumnext/highlightjs"; | import { ensureMimeTypes, highlight, highlightAuto, loadTheme, Themes, type AutoHighlightResult, type HighlightResult, type Theme } 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"; | ||||||
|  | import toast from "./toast.js"; | ||||||
|  | import { t } from "./i18n.js"; | ||||||
| 
 | 
 | ||||||
| let highlightingLoaded = false; | let highlightingLoaded = false; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Identifies all the code blocks (as `pre code`) under the specified hierarchy and uses the highlight.js library to obtain the highlighted text which is then applied on to the code blocks. |  * 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. | ||||||
|  |  * Additionally, adds a "Copy to clipboard" button. | ||||||
|  * |  * | ||||||
|  * @param $container the container under which to look for code blocks and to apply syntax highlighting to them. |  * @param $container the container under which to look for code blocks and to apply syntax highlighting to them. | ||||||
|  */ |  */ | ||||||
| export async function applySyntaxHighlight($container: JQuery<HTMLElement>) { | export async function formatCodeBlocks($container: JQuery<HTMLElement>) { | ||||||
|     if (!isSyntaxHighlightEnabled()) { |     const syntaxHighlightingEnabled = isSyntaxHighlightEnabled(); | ||||||
|         return; |     if (syntaxHighlightingEnabled) { | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|         await ensureMimeTypesForHighlighting(); |         await ensureMimeTypesForHighlighting(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     const codeBlocks = $container.find("pre code"); |     const codeBlocks = $container.find("pre code"); | ||||||
|     for (const codeBlock of codeBlocks) { |     for (const codeBlock of codeBlocks) { | ||||||
| @ -23,8 +25,29 @@ export async function applySyntaxHighlight($container: JQuery<HTMLElement>) { | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         applyCopyToClipboardButton($(codeBlock)); | ||||||
|  | 
 | ||||||
|  |         if (syntaxHighlightingEnabled) { | ||||||
|             applySingleBlockSyntaxHighlight($(codeBlock), normalizedMimeType); |             applySingleBlockSyntaxHighlight($(codeBlock), normalizedMimeType); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function applyCopyToClipboardButton($codeBlock: JQuery<HTMLElement>) { | ||||||
|  |     const $copyButton = $("<button>") | ||||||
|  |         .addClass("bx component btn tn-tool-button bx-copy copy-button") | ||||||
|  |         .attr("title", t("code_block.copy_title")) | ||||||
|  |         .on("click", () => { | ||||||
|  |             const text = $codeBlock.text(); | ||||||
|  | 
 | ||||||
|  |             try { | ||||||
|  |                 navigator.clipboard.writeText(text); | ||||||
|  |                 toast.showMessage(t("code_block.copy_success")); | ||||||
|  |             } catch (e) { | ||||||
|  |                 toast.showError(t("code_block.copy_failed")); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     $codeBlock.parent().append($copyButton); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -34,7 +57,7 @@ export async function applySingleBlockSyntaxHighlight($codeBlock: JQuery<HTMLEle | |||||||
|     $codeBlock.parent().toggleClass("hljs"); |     $codeBlock.parent().toggleClass("hljs"); | ||||||
|     const text = $codeBlock.text(); |     const text = $codeBlock.text(); | ||||||
| 
 | 
 | ||||||
|     let highlightedText = null; |     let highlightedText: HighlightResult | AutoHighlightResult | null = null; | ||||||
|     if (normalizedMimeType === mime_types.MIME_TYPE_AUTO) { |     if (normalizedMimeType === mime_types.MIME_TYPE_AUTO) { | ||||||
|         await ensureMimeTypesForHighlighting(); |         await ensureMimeTypesForHighlighting(); | ||||||
|         highlightedText = highlightAuto(text); |         highlightedText = highlightAuto(text); | ||||||
| @ -66,7 +89,7 @@ export async function ensureMimeTypesForHighlighting() { | |||||||
| 
 | 
 | ||||||
| export function loadHighlightingTheme(themeName: string) { | export function loadHighlightingTheme(themeName: string) { | ||||||
|     const themePrefix = "default:"; |     const themePrefix = "default:"; | ||||||
|     let theme = null; |     let theme: Theme | null = null; | ||||||
|     if (themeName.includes(themePrefix)) { |     if (themeName.includes(themePrefix)) { | ||||||
|         theme = Themes[themeName.substring(themePrefix.length)]; |         theme = Themes[themeName.substring(themePrefix.length)]; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -529,6 +529,26 @@ pre:not(.hljs) { | |||||||
|     font-size: 100%; |     font-size: 100%; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | pre { | ||||||
|  |     position: relative; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pre > button.copy-button { | ||||||
|  |     position: absolute; | ||||||
|  |     top: 1em; | ||||||
|  |     right: 1em; | ||||||
|  |     opacity: 0.8;     | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pre > button.copy-button:hover { | ||||||
|  |     color: inherit !important; | ||||||
|  |     opacity: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pre > button.copy-button:active { | ||||||
|  |     background-color: unset !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .pointer { | .pointer { | ||||||
|     cursor: pointer; |     cursor: pointer; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1830,7 +1830,10 @@ | |||||||
|     "word_wrapping": "Word wrapping", |     "word_wrapping": "Word wrapping", | ||||||
|     "theme_none": "No syntax highlighting", |     "theme_none": "No syntax highlighting", | ||||||
|     "theme_group_light": "Light themes", |     "theme_group_light": "Light themes", | ||||||
|     "theme_group_dark": "Dark themes" |     "theme_group_dark": "Dark themes", | ||||||
|  |     "copy_success": "The code block was copied to clipboard.", | ||||||
|  |     "copy_failed": "The code block could not be copied to the clipboard due to lack of permissions.", | ||||||
|  |     "copy_title": "Copy to clipboard" | ||||||
|   }, |   }, | ||||||
|   "classic_editor_toolbar": { |   "classic_editor_toolbar": { | ||||||
|     "title": "Formatting" |     "title": "Formatting" | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								apps/client/src/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								apps/client/src/types.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -93,16 +93,6 @@ declare global { | |||||||
|         getSelectedExternalLink(): string | undefined; |         getSelectedExternalLink(): string | undefined; | ||||||
|         setSelectedExternalLink(externalLink: string | null | undefined); |         setSelectedExternalLink(externalLink: string | null | undefined); | ||||||
|         setNote(noteId: string); |         setNote(noteId: string); | ||||||
|         markRegExp(regex: RegExp, opts: { |  | ||||||
|             element: string; |  | ||||||
|             className: string; |  | ||||||
|             separateWordSearch: boolean; |  | ||||||
|             caseSensitive: boolean; |  | ||||||
|             done?: () => void; |  | ||||||
|         }); |  | ||||||
|         unmark(opts?: { |  | ||||||
|             done: () => void; |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     interface JQueryStatic { |     interface JQueryStatic { | ||||||
|  | |||||||
| @ -248,10 +248,10 @@ export default class FindWidget extends NoteContextAwareWidget { | |||||||
|             case "code": |             case "code": | ||||||
|                 return this.codeHandler; |                 return this.codeHandler; | ||||||
|             case "text": |             case "text": | ||||||
|                 return this.textHandler; |  | ||||||
|             default: |  | ||||||
|                 const readOnly = await this.noteContext?.isReadOnly(); |                 const readOnly = await this.noteContext?.isReadOnly(); | ||||||
|                 return readOnly ? this.htmlHandler : this.textHandler; |                 return readOnly ? this.htmlHandler : this.textHandler; | ||||||
|  |             default: | ||||||
|  |                 console.warn("FindWidget: Unsupported note type for find widget", this.note?.type); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| // ck-find-result and ck-find-result_selected are the styles ck-editor
 | // ck-find-result and ck-find-result_selected are the styles ck-editor
 | ||||||
| // uses for highlighting matches, use the same one on CodeMirror
 | // uses for highlighting matches, use the same one on CodeMirror
 | ||||||
| // for consistency
 | // for consistency
 | ||||||
|  | import type Mark from "mark.js"; | ||||||
| import utils from "../services/utils.js"; | import utils from "../services/utils.js"; | ||||||
| import type FindWidget from "./find.js"; | import type FindWidget from "./find.js"; | ||||||
| import type { FindResult } from "./find.js"; | import type { FindResult } from "./find.js"; | ||||||
| @ -13,6 +14,7 @@ export default class FindInHtml { | |||||||
|     private parent: FindWidget; |     private parent: FindWidget; | ||||||
|     private currentIndex: number; |     private currentIndex: number; | ||||||
|     private $results: JQuery<HTMLElement> | null; |     private $results: JQuery<HTMLElement> | null; | ||||||
|  |     private mark?: Mark; | ||||||
| 
 | 
 | ||||||
|     constructor(parent: FindWidget) { |     constructor(parent: FindWidget) { | ||||||
|         this.parent = parent; |         this.parent = parent; | ||||||
| @ -21,21 +23,24 @@ export default class FindInHtml { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async performFind(searchTerm: string, matchCase: boolean, wholeWord: boolean) { |     async performFind(searchTerm: string, matchCase: boolean, wholeWord: boolean) { | ||||||
|         await import("mark.js"); |  | ||||||
| 
 |  | ||||||
|         const $content = await this.parent?.noteContext?.getContentElement(); |         const $content = await this.parent?.noteContext?.getContentElement(); | ||||||
|  |         if (!$content || !$content.length) { | ||||||
|  |             return Promise.resolve({ totalFound: 0, currentFound: 0 }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!this.mark) { | ||||||
|  |             this.mark = new (await import("mark.js")).default($content[0]); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         const wholeWordChar = wholeWord ? "\\b" : ""; |         const wholeWordChar = wholeWord ? "\\b" : ""; | ||||||
|         const regExp = new RegExp(wholeWordChar + utils.escapeRegExp(searchTerm) + wholeWordChar, matchCase ? "g" : "gi"); |         const regExp = new RegExp(wholeWordChar + utils.escapeRegExp(searchTerm) + wholeWordChar, matchCase ? "g" : "gi"); | ||||||
| 
 | 
 | ||||||
|         return new Promise<FindResult>((res) => { |         return new Promise<FindResult>((res) => { | ||||||
|             $content?.unmark({ |             this.mark!.unmark({ | ||||||
|                 done: () => { |                 done: () => { | ||||||
|                     $content.markRegExp(regExp, { |                     this.mark!.markRegExp(regExp, { | ||||||
|                         element: "span", |                         element: "span", | ||||||
|                         className: FIND_RESULT_CSS_CLASSNAME, |                         className: FIND_RESULT_CSS_CLASSNAME, | ||||||
|                         separateWordSearch: false, |  | ||||||
|                         caseSensitive: matchCase, |  | ||||||
|                         done: async () => { |                         done: async () => { | ||||||
|                             this.$results = $content.find(`.${FIND_RESULT_CSS_CLASSNAME}`); |                             this.$results = $content.find(`.${FIND_RESULT_CSS_CLASSNAME}`); | ||||||
|                             const scrollingContainer = $content[0].closest('.scrolling-container'); |                             const scrollingContainer = $content[0].closest('.scrolling-container'); | ||||||
| @ -73,10 +78,7 @@ export default class FindInHtml { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async findBoxClosed(totalFound: number, currentFound: number) { |     async findBoxClosed(totalFound: number, currentFound: number) { | ||||||
|         const $content = await this.parent?.noteContext?.getContentElement(); |         this.mark?.unmark(); | ||||||
|         if (typeof $content?.unmark === 'function') { |  | ||||||
|             $content.unmark(); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async jumpTo() { |     async jumpTo() { | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ import { createChatSession, checkSessionExists, setupStreamingResponse, getDirec | |||||||
| import { extractInChatToolSteps } from "./message_processor.js"; | import { extractInChatToolSteps } from "./message_processor.js"; | ||||||
| import { validateEmbeddingProviders } from "./validation.js"; | import { validateEmbeddingProviders } from "./validation.js"; | ||||||
| import type { MessageData, ToolExecutionStep, ChatData } from "./types.js"; | import type { MessageData, ToolExecutionStep, ChatData } from "./types.js"; | ||||||
| import { applySyntaxHighlight } from "../../services/syntax_highlight.js"; | import { formatCodeBlocks } from "../../services/syntax_highlight.js"; | ||||||
| 
 | 
 | ||||||
| import "../../stylesheets/llm_chat.css"; | import "../../stylesheets/llm_chat.css"; | ||||||
| 
 | 
 | ||||||
| @ -925,7 +925,7 @@ export default class LlmChatPanel extends BasicWidget { | |||||||
| 
 | 
 | ||||||
|         // Apply syntax highlighting if this is the final update
 |         // Apply syntax highlighting if this is the final update
 | ||||||
|         if (isDone) { |         if (isDone) { | ||||||
|             applySyntaxHighlight($(assistantMessageEl as HTMLElement)); |             formatCodeBlocks($(assistantMessageEl as HTMLElement)); | ||||||
| 
 | 
 | ||||||
|             // Update message in the data model for storage
 |             // Update message in the data model for storage
 | ||||||
|             // Find the last assistant message to update, or add a new one if none exists
 |             // Find the last assistant message to update, or add a new one if none exists
 | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
|  * Utility functions for LLM Chat |  * Utility functions for LLM Chat | ||||||
|  */ |  */ | ||||||
| import { marked } from "marked"; | import { marked } from "marked"; | ||||||
| import { applySyntaxHighlight } from "../../services/syntax_highlight.js"; | import { formatCodeBlocks } from "../../services/syntax_highlight.js"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Format markdown content for display |  * Format markdown content for display | ||||||
| @ -62,7 +62,7 @@ export function escapeHtml(text: string): string { | |||||||
|  * Apply syntax highlighting to content |  * Apply syntax highlighting to content | ||||||
|  */ |  */ | ||||||
| export function applyHighlighting(element: HTMLElement): void { | export function applyHighlighting(element: HTMLElement): void { | ||||||
|     applySyntaxHighlight($(element)); |     formatCodeBlocks($(element)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import AbstractTextTypeWidget from "./abstract_text_type_widget.js"; | import AbstractTextTypeWidget from "./abstract_text_type_widget.js"; | ||||||
| import { applySyntaxHighlight } from "../../services/syntax_highlight.js"; | import { formatCodeBlocks } from "../../services/syntax_highlight.js"; | ||||||
| import type FNote from "../../entities/fnote.js"; | import type FNote from "../../entities/fnote.js"; | ||||||
| import type { CommandListenerData, EventData } from "../../components/app_context.js"; | import type { CommandListenerData, EventData } from "../../components/app_context.js"; | ||||||
| import { getLocaleById } from "../../services/i18n.js"; | import { getLocaleById } from "../../services/i18n.js"; | ||||||
| @ -125,7 +125,7 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         await this.#applyInlineMermaid(); |         await this.#applyInlineMermaid(); | ||||||
|         await applySyntaxHighlight(this.$content); |         await formatCodeBlocks(this.$content); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async #applyInlineMermaid() { |     async #applyInlineMermaid() { | ||||||
|  | |||||||
| @ -215,8 +215,6 @@ class ListOrGridView extends ViewMode { | |||||||
| 
 | 
 | ||||||
|         const highlightedTokens = this.parentNote.highlightedTokens || []; |         const highlightedTokens = this.parentNote.highlightedTokens || []; | ||||||
|         if (highlightedTokens.length > 0) { |         if (highlightedTokens.length > 0) { | ||||||
|             await import("mark.js"); |  | ||||||
| 
 |  | ||||||
|             const regex = highlightedTokens.map((token) => utils.escapeRegExp(token)).join("|"); |             const regex = highlightedTokens.map((token) => utils.escapeRegExp(token)).join("|"); | ||||||
| 
 | 
 | ||||||
|             this.highlightRegex = new RegExp(regex, "gi"); |             this.highlightRegex = new RegExp(regex, "gi"); | ||||||
| @ -320,11 +318,10 @@ class ListOrGridView extends ViewMode { | |||||||
|         $expander.on("click", () => this.toggleContent($card, note, !$card.hasClass("expanded"))); |         $expander.on("click", () => this.toggleContent($card, note, !$card.hasClass("expanded"))); | ||||||
| 
 | 
 | ||||||
|         if (this.highlightRegex) { |         if (this.highlightRegex) { | ||||||
|             $card.find(".note-book-title").markRegExp(this.highlightRegex, { |             const Mark = new (await import("mark.js")).default($card.find(".note-book-title")[0]); | ||||||
|  |             Mark.markRegExp(this.highlightRegex, { | ||||||
|                 element: "span", |                 element: "span", | ||||||
|                 className: "ck-find-result", |                 className: "ck-find-result" | ||||||
|                 separateWordSearch: false, |  | ||||||
|                 caseSensitive: false |  | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -362,11 +359,10 @@ class ListOrGridView extends ViewMode { | |||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|             if (this.highlightRegex) { |             if (this.highlightRegex) { | ||||||
|                 $renderedContent.markRegExp(this.highlightRegex, { |                 const Mark = new (await import("mark.js")).default($renderedContent[0]); | ||||||
|  |                 Mark.markRegExp(this.highlightRegex, { | ||||||
|                     element: "span", |                     element: "span", | ||||||
|                     className: "ck-find-result", |                     className: "ck-find-result" | ||||||
|                     separateWordSearch: false, |  | ||||||
|                     caseSensitive: false |  | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -68,8 +68,8 @@ module.exports = { | |||||||
|         ] |         ] | ||||||
|     }, |     }, | ||||||
|     rebuildConfig: { |     rebuildConfig: { | ||||||
|         force: false, |         force: true, | ||||||
|         onlyModules: [] |         extraModules: [ "better-sqlite3" ] | ||||||
|     }, |     }, | ||||||
|     makers: [ |     makers: [ | ||||||
|         { |         { | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ | |||||||
|     "@types/electron-squirrel-startup": "1.0.2", |     "@types/electron-squirrel-startup": "1.0.2", | ||||||
|     "@triliumnext/server": "workspace:*", |     "@triliumnext/server": "workspace:*", | ||||||
|     "copy-webpack-plugin": "13.0.0", |     "copy-webpack-plugin": "13.0.0", | ||||||
|     "electron": "36.2.1", |     "electron": "36.3.1", | ||||||
|     "@electron-forge/cli": "7.8.1", |     "@electron-forge/cli": "7.8.1", | ||||||
|     "@electron-forge/maker-deb": "7.8.1", |     "@electron-forge/maker-deb": "7.8.1", | ||||||
|     "@electron-forge/maker-dmg": "7.8.1", |     "@electron-forge/maker-dmg": "7.8.1", | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ | |||||||
|     "@triliumnext/desktop": "workspace:*", |     "@triliumnext/desktop": "workspace:*", | ||||||
|     "@types/fs-extra": "11.0.4", |     "@types/fs-extra": "11.0.4", | ||||||
|     "copy-webpack-plugin": "13.0.0", |     "copy-webpack-plugin": "13.0.0", | ||||||
|     "electron": "36.2.1", |     "electron": "36.3.1", | ||||||
|     "fs-extra": "11.3.0" |     "fs-extra": "11.3.0" | ||||||
|   }, |   }, | ||||||
|   "nx": { |   "nx": { | ||||||
|  | |||||||
| @ -33,24 +33,24 @@ if (!DOCS_ROOT || !USER_GUIDE_ROOT) { | |||||||
| const NOTE_MAPPINGS: NoteMapping[] = [ | const NOTE_MAPPINGS: NoteMapping[] = [ | ||||||
|     { |     { | ||||||
|         rootNoteId: "pOsGYCXsbNQG", |         rootNoteId: "pOsGYCXsbNQG", | ||||||
|         path: path.join(DOCS_ROOT, "User Guide"), |         path: path.join(__dirname, DOCS_ROOT, "User Guide"), | ||||||
|         format: "markdown" |         format: "markdown" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         rootNoteId: "pOsGYCXsbNQG", |         rootNoteId: "pOsGYCXsbNQG", | ||||||
|         path: USER_GUIDE_ROOT, |         path: path.join(__dirname, USER_GUIDE_ROOT), | ||||||
|         format: "html", |         format: "html", | ||||||
|         ignoredFiles: ["index.html", "navigation.html", "style.css", "User Guide.html"], |         ignoredFiles: ["index.html", "navigation.html", "style.css", "User Guide.html"], | ||||||
|         exportOnly: true |         exportOnly: true | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         rootNoteId: "jdjRLhLV3TtI", |         rootNoteId: "jdjRLhLV3TtI", | ||||||
|         path: path.join(DOCS_ROOT, "Developer Guide"), |         path: path.join(__dirname, DOCS_ROOT, "Developer Guide"), | ||||||
|         format: "markdown" |         format: "markdown" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         rootNoteId: "hD3V4hiu2VW4", |         rootNoteId: "hD3V4hiu2VW4", | ||||||
|         path: path.join(DOCS_ROOT, "Release Notes"), |         path: path.join(__dirname, DOCS_ROOT, "Release Notes"), | ||||||
|         format: "markdown" |         format: "markdown" | ||||||
|     } |     } | ||||||
| ]; | ]; | ||||||
|  | |||||||
| @ -59,7 +59,7 @@ | |||||||
|     "debounce": "2.2.0", |     "debounce": "2.2.0", | ||||||
|     "debug": "4.4.1", |     "debug": "4.4.1", | ||||||
|     "ejs": "3.1.10", |     "ejs": "3.1.10", | ||||||
|     "electron": "36.2.1", |     "electron": "36.3.1", | ||||||
|     "electron-debug": "4.1.0", |     "electron-debug": "4.1.0", | ||||||
|     "electron-window-state": "5.0.3", |     "electron-window-state": "5.0.3", | ||||||
|     "escape-html": "1.0.3", |     "escape-html": "1.0.3", | ||||||
| @ -115,8 +115,13 @@ | |||||||
|       "serve": { |       "serve": { | ||||||
|         "executor": "@nx/js:node", |         "executor": "@nx/js:node", | ||||||
|         "dependsOn": [ |         "dependsOn": [ | ||||||
|  |           { | ||||||
|  |             "projects": [ "client" ], | ||||||
|  |             "target": "serve" | ||||||
|  |           }, | ||||||
|           "build-without-client" |           "build-without-client" | ||||||
|         ], |         ], | ||||||
|  |         "continuous": true, | ||||||
|         "options": { |         "options": { | ||||||
|           "buildTarget": "server:build-without-client:development", |           "buildTarget": "server:build-without-client:development", | ||||||
|           "runBuildTargetDependencies": false |           "runBuildTargetDependencies": false | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| <p>Trilium supports configuration via a file named <code>config.ini</code> and | <p>Trilium supports configuration via a file named <code>config.ini</code> and | ||||||
|   environment variables. Please review the file named <a href="https://github.com/TriliumNext/Notes/blob/develop/config-sample.ini">config-sample.ini</a> in |   environment variables. Please review the file named <a href="https://github.com/TriliumNext/Notes/blob/develop/apps/server/src/assets/config-sample.ini">config-sample.ini</a> in | ||||||
|   the <a href="https://github.com/TriliumNext/Notes">Notes</a> repository to |   the <a href="https://github.com/TriliumNext/Notes">Notes</a> repository to | ||||||
|   see what values are supported.</p> |   see what values are supported.</p> | ||||||
| <p>You can provide the same values via environment variables instead of the <code>config.ini</code> file, | <p>You can provide the same values via environment variables instead of the <code>config.ini</code> file, | ||||||
|  | |||||||
| @ -49,6 +49,9 @@ | |||||||
| *   Backend log: disable some editor features in order to increase performance for large logs (syntax highlighting, folding, etc.). | *   Backend log: disable some editor features in order to increase performance for large logs (syntax highlighting, folding, etc.). | ||||||
| *   [Collapsible table of contents](https://github.com/TriliumNext/Notes/pull/1954) by @SiriusXT | *   [Collapsible table of contents](https://github.com/TriliumNext/Notes/pull/1954) by @SiriusXT | ||||||
| *   Sessions (logins) are no longer stored as files in the data directory, but as entries in the database. This improves the session reliability on Windows platforms. | *   Sessions (logins) are no longer stored as files in the data directory, but as entries in the database. This improves the session reliability on Windows platforms. | ||||||
|  | *   Code blocks in text notes: | ||||||
|  |     *   For editable notes, clicking on a code block will reveal a toolbar with a way to easily change the programming language and another button to copy the text to clipboard. | ||||||
|  |     *   For read-only notes, a floating button allows copying the code snippet to clipboard. | ||||||
| 
 | 
 | ||||||
| ## 📖 Documentation | ## 📖 Documentation | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| # Configuration (config.ini or environment variables) | # Configuration (config.ini or environment variables) | ||||||
| Trilium supports configuration via a file named `config.ini` and environment variables. Please review the file named [config-sample.ini](https://github.com/TriliumNext/Notes/blob/develop/config-sample.ini) in the [Notes](https://github.com/TriliumNext/Notes) repository to see what values are supported. | Trilium supports configuration via a file named `config.ini` and environment variables. Please review the file named [config-sample.ini](https://github.com/TriliumNext/Notes/blob/develop/apps/server/src/assets/config-sample.ini) in the [Notes](https://github.com/TriliumNext/Notes) repository to see what values are supported. | ||||||
| 
 | 
 | ||||||
| You can provide the same values via environment variables instead of the `config.ini` file, and these environment variables use the following format: | You can provide the same values via environment variables instead of the `config.ini` file, and these environment variables use the following format: | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ | |||||||
|     "server:test": "nx test server", |     "server:test": "nx test server", | ||||||
|     "server:build": "nx build server", |     "server:build": "nx build server", | ||||||
|     "server:coverage": "nx test server --coverage", |     "server:coverage": "nx test server --coverage", | ||||||
|     "server:start": "nx run-many --target=serve --projects=client,server --parallel", |     "server:start": "nx run server:serve", | ||||||
|     "server:start-prod": "nx run server:start-prod", |     "server:start-prod": "nx run server:start-prod", | ||||||
|     "electron:build": "nx build desktop", |     "electron:build": "nx build desktop", | ||||||
|     "chore:ci-update-nightly-version": "tsx ./scripts/update-nightly-version.ts", |     "chore:ci-update-nightly-version": "tsx ./scripts/update-nightly-version.ts", | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								packages/ckeditor5/src/icons/copy.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/ckeditor5/src/icons/copy.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" style="fill: rgba(0, 0, 0, 1);transform: ;msFilter:;"><path d="M20 2H10c-1.103 0-2 .897-2 2v4H4c-1.103 0-2 .897-2 2v10c0 1.103.897 2 2 2h10c1.103 0 2-.897 2-2v-4h4c1.103 0 2-.897 2-2V4c0-1.103-.897-2-2-2zM4 20V10h10l.002 10H4zm16-6h-4v-4c0-1.103-.897-2-2-2h-4V4h10v10z"></path></svg> | ||||||
| After Width: | Height: | Size: 366 B | 
| @ -1,4 +1,5 @@ | |||||||
| import "ckeditor5/ckeditor5.css"; | import "ckeditor5/ckeditor5.css"; | ||||||
|  | import "./theme/code_block_toolbar.css"; | ||||||
| import { COMMON_PLUGINS, CORE_PLUGINS, POPUP_EDITOR_PLUGINS } from "./plugins"; | import { COMMON_PLUGINS, CORE_PLUGINS, POPUP_EDITOR_PLUGINS } from "./plugins"; | ||||||
| import { BalloonEditor, DecoupledEditor, FindAndReplaceEditing, FindCommand } from "ckeditor5"; | import { BalloonEditor, DecoupledEditor, FindAndReplaceEditing, FindCommand } from "ckeditor5"; | ||||||
| export { EditorWatchdog } from "ckeditor5"; | export { EditorWatchdog } from "ckeditor5"; | ||||||
|  | |||||||
| @ -23,6 +23,8 @@ import "@triliumnext/ckeditor5-mermaid/index.css"; | |||||||
| import "@triliumnext/ckeditor5-admonition/index.css"; | import "@triliumnext/ckeditor5-admonition/index.css"; | ||||||
| import "@triliumnext/ckeditor5-footnotes/index.css"; | import "@triliumnext/ckeditor5-footnotes/index.css"; | ||||||
| import "@triliumnext/ckeditor5-math/index.css"; | import "@triliumnext/ckeditor5-math/index.css"; | ||||||
|  | import CodeBlockToolbar from "./plugins/code_block_toolbar.js"; | ||||||
|  | import CodeBlockLanguageDropdown from "./plugins/code_block_language_dropdown.js"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Plugins that are specific to Trilium and not part of the CKEditor 5 core, included in both text editors but not in the attribute editor. |  * Plugins that are specific to Trilium and not part of the CKEditor 5 core, included in both text editors but not in the attribute editor. | ||||||
| @ -38,7 +40,9 @@ const TRILIUM_PLUGINS: typeof Plugin[] = [ | |||||||
|     MarkdownImportPlugin, |     MarkdownImportPlugin, | ||||||
|     IncludeNote, |     IncludeNote, | ||||||
|     Uploadfileplugin, |     Uploadfileplugin, | ||||||
|     SyntaxHighlighting |     SyntaxHighlighting, | ||||||
|  |     CodeBlockLanguageDropdown, | ||||||
|  |     CodeBlockToolbar | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | |||||||
							
								
								
									
										103
									
								
								packages/ckeditor5/src/plugins/code_block_language_dropdown.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								packages/ckeditor5/src/plugins/code_block_language_dropdown.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | |||||||
|  | import { Editor, CodeBlock, Plugin, type ListDropdownButtonDefinition, Collection, type CodeBlockCommand, ViewModel, createDropdown, addListToDropdown, DropdownButtonView } from "ckeditor5"; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Toolbar item which displays the list of languages in a dropdown, with the text visible (similar to the headings switcher), as opposed to the default split button implementation. | ||||||
|  |  */ | ||||||
|  | export default class CodeBlockLanguageDropdown extends Plugin { | ||||||
|  | 
 | ||||||
|  |     static get requires() { | ||||||
|  |         return [ CodeBlock ]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public init() { | ||||||
|  |         const editor = this.editor; | ||||||
|  |         const componentFactory = editor.ui.componentFactory; | ||||||
|  | 
 | ||||||
|  |         const normalizedLanguageDefs = this._getNormalizedAndLocalizedLanguageDefinitions(editor); | ||||||
|  |         const itemDefinitions = this._getLanguageListItemDefinitions(normalizedLanguageDefs); | ||||||
|  |         const command: CodeBlockCommand = editor.commands.get( 'codeBlock' )!; | ||||||
|  | 
 | ||||||
|  |         componentFactory.add("codeBlockDropdown", locale => { | ||||||
|  |             const dropdownView = createDropdown(this.editor.locale, DropdownButtonView); | ||||||
|  |             dropdownView.buttonView.set({ | ||||||
|  |                 withText: true | ||||||
|  |             }); | ||||||
|  |             dropdownView.bind( 'isEnabled' ).to( command, 'value', value => !!value ); | ||||||
|  |             dropdownView.buttonView.bind( 'label' ).to( command, 'value', (value) => { | ||||||
|  |                 const itemDefinition = normalizedLanguageDefs.find((def) => def.language === value); | ||||||
|  |                 return itemDefinition?.label; | ||||||
|  |             }); | ||||||
|  |             dropdownView.on( 'execute', evt => { | ||||||
|  |                 editor.execute( 'codeBlock', { | ||||||
|  |                     language: ( evt.source as any )._codeBlockLanguage, | ||||||
|  |                     forceValue: true | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |                 editor.editing.view.focus(); | ||||||
|  |             }); | ||||||
|  |             addListToDropdown(dropdownView, itemDefinitions); | ||||||
|  |             return dropdownView; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Adapted from packages/ckeditor5-code-block/src/codeblockui.ts
 | ||||||
|  |     private _getLanguageListItemDefinitions( | ||||||
|  | 		normalizedLanguageDefs: Array<CodeBlockLanguageDefinition> | ||||||
|  | 	): Collection<ListDropdownButtonDefinition> { | ||||||
|  | 		const editor = this.editor; | ||||||
|  | 		const command: CodeBlockCommand = editor.commands.get( 'codeBlock' )!; | ||||||
|  | 		const itemDefinitions = new Collection<ListDropdownButtonDefinition>(); | ||||||
|  | 
 | ||||||
|  | 		for ( const languageDef of normalizedLanguageDefs ) { | ||||||
|  | 			const definition: ListDropdownButtonDefinition = { | ||||||
|  | 				type: 'button', | ||||||
|  | 				model: new ViewModel( { | ||||||
|  | 					_codeBlockLanguage: languageDef.language, | ||||||
|  | 					label: languageDef.label, | ||||||
|  | 					role: 'menuitemradio', | ||||||
|  | 					withText: true | ||||||
|  | 				} ) | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			definition.model.bind( 'isOn' ).to( command, 'value', value => { | ||||||
|  | 				return value === definition.model._codeBlockLanguage; | ||||||
|  | 			} ); | ||||||
|  | 
 | ||||||
|  | 			itemDefinitions.add( definition ); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return itemDefinitions; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  |     // Adapted from packages/ckeditor5-code-block/src/utils.ts
 | ||||||
|  |     private _getNormalizedAndLocalizedLanguageDefinitions(editor: Editor) { | ||||||
|  |         const languageDefs = editor.config.get( 'codeBlock.languages' ) as Array<CodeBlockLanguageDefinition>; | ||||||
|  |         for ( const def of languageDefs ) { | ||||||
|  |             if ( def.class === undefined ) { | ||||||
|  |                 def.class = `language-${ def.language }`; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return languageDefs; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface CodeBlockLanguageDefinition { | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * The name of the language that will be stored in the model attribute. Also, when `class` | ||||||
|  | 	 * is not specified, it will be used to create the CSS class associated with the language (prefixed by "language-"). | ||||||
|  | 	 */ | ||||||
|  | 	language: string; | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * The human–readable label associated with the language and displayed in the UI. | ||||||
|  | 	 */ | ||||||
|  | 	label: string; | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * The CSS class associated with the language. When not specified the `language` | ||||||
|  |  	 * property is used to create a class prefixed by "language-". | ||||||
|  | 	 */ | ||||||
|  | 	class?: string; | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								packages/ckeditor5/src/plugins/code_block_toolbar.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								packages/ckeditor5/src/plugins/code_block_toolbar.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | import { CodeBlock, Plugin, ViewDocumentFragment, WidgetToolbarRepository, type ViewNode } from "ckeditor5"; | ||||||
|  | import CodeBlockLanguageDropdown from "./code_block_language_dropdown"; | ||||||
|  | import CopyToClipboardButton from "./copy_to_clipboard_button"; | ||||||
|  | 
 | ||||||
|  | export default class CodeBlockToolbar extends Plugin { | ||||||
|  | 
 | ||||||
|  |     static get requires() { | ||||||
|  |         return [ WidgetToolbarRepository, CodeBlock, CodeBlockLanguageDropdown, CopyToClipboardButton ] as const; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     afterInit() { | ||||||
|  |         const editor = this.editor; | ||||||
|  |         const widgetToolbarRepository = editor.plugins.get(WidgetToolbarRepository); | ||||||
|  | 
 | ||||||
|  |         widgetToolbarRepository.register("codeblock", { | ||||||
|  |             items: [ | ||||||
|  |                 "codeBlockDropdown", | ||||||
|  |                 "|", | ||||||
|  |                 "copyToClipboard" | ||||||
|  |             ], | ||||||
|  |             balloonClassName: "ck-toolbar-container codeblock-language-list", | ||||||
|  |             getRelatedElement(selection) { | ||||||
|  |                 const selectionPosition = selection.getFirstPosition(); | ||||||
|  |                 if (!selectionPosition) { | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 let parent: ViewNode | ViewDocumentFragment | null = selectionPosition.parent; | ||||||
|  |                 while (parent) { | ||||||
|  |                     if (parent.is("element", "pre")) { | ||||||
|  |                         return parent; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     parent = parent.parent; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								packages/ckeditor5/src/plugins/copy_to_clipboard_button.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								packages/ckeditor5/src/plugins/copy_to_clipboard_button.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | |||||||
|  | import { ButtonView, Command, Plugin } from "ckeditor5"; | ||||||
|  | import copyIcon from "../icons/copy.svg?raw"; | ||||||
|  | 
 | ||||||
|  | export default class CopyToClipboardButton extends Plugin { | ||||||
|  | 
 | ||||||
|  |     public init() { | ||||||
|  |         const editor = this.editor; | ||||||
|  |         editor.commands.add("copyToClipboard", new CopyToClipboardCommand(this.editor)); | ||||||
|  | 
 | ||||||
|  |         const componentFactory = editor.ui.componentFactory; | ||||||
|  |         componentFactory.add("copyToClipboard", locale => { | ||||||
|  |             const button = new ButtonView(locale); | ||||||
|  |             button.set({ | ||||||
|  |                 tooltip: "Copy to clipboard", | ||||||
|  |                 icon: copyIcon | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             this.listenTo(button, "execute", () => { | ||||||
|  |                 editor.execute("copyToClipboard"); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             return button; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class CopyToClipboardCommand extends Command { | ||||||
|  | 
 | ||||||
|  |     execute(...args: Array<unknown>) { | ||||||
|  |         const editor = this.editor; | ||||||
|  |         const model = editor.model; | ||||||
|  |         const selection = model.document.selection; | ||||||
|  | 
 | ||||||
|  |         const codeBlockEl = selection.getFirstPosition()?.findAncestor("codeBlock"); | ||||||
|  |         if (!codeBlockEl) { | ||||||
|  |             console.warn("Unable to find code block element to copy from."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const codeText = Array.from(codeBlockEl.getChildren()) | ||||||
|  |             .map(child => "data" in child ? child.data : "\n") | ||||||
|  |             .join(""); | ||||||
|  | 
 | ||||||
|  |         if (codeText) { | ||||||
|  |             navigator.clipboard.writeText(codeText).then(() => { | ||||||
|  |                 console.log('Code block copied to clipboard'); | ||||||
|  |             }).catch(err => { | ||||||
|  |                 console.error('Failed to copy code block', err); | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             console.warn('No code block selected or found.'); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								packages/ckeditor5/src/theme/code_block_toolbar.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								packages/ckeditor5/src/theme/code_block_toolbar.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | .ck.ck-balloon-panel.codeblock-language-list .ck-dropdown__panel { | ||||||
|  |     max-height: 300px; | ||||||
|  |     overflow-y: auto; | ||||||
|  | } | ||||||
| @ -3,6 +3,7 @@ import { normalizeMimeTypeForCKEditor, type MimeType } from "@triliumnext/common | |||||||
| import syntaxDefinitions from "./syntax_highlighting.js"; | import syntaxDefinitions from "./syntax_highlighting.js"; | ||||||
| import { type Theme } from "./themes.js"; | import { type Theme } from "./themes.js"; | ||||||
| import { type HighlightOptions } from "highlight.js"; | import { type HighlightOptions } from "highlight.js"; | ||||||
|  | export type { HighlightResult, AutoHighlightResult } from "highlight.js"; | ||||||
| 
 | 
 | ||||||
| export { default as Themes, type Theme } from "./themes.js"; | export { default as Themes, type Theme } from "./themes.js"; | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										26
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										26
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -351,7 +351,7 @@ importers: | |||||||
|     dependencies: |     dependencies: | ||||||
|       '@electron/remote': |       '@electron/remote': | ||||||
|         specifier: 2.1.2 |         specifier: 2.1.2 | ||||||
|         version: 2.1.2(electron@36.2.1) |         version: 2.1.2(electron@36.3.1) | ||||||
|       better-sqlite3: |       better-sqlite3: | ||||||
|         specifier: ^11.9.1 |         specifier: ^11.9.1 | ||||||
|         version: 11.10.0 |         version: 11.10.0 | ||||||
| @ -405,8 +405,8 @@ importers: | |||||||
|         specifier: 13.0.0 |         specifier: 13.0.0 | ||||||
|         version: 13.0.0(webpack@5.99.9(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.4)) |         version: 13.0.0(webpack@5.99.9(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.4)) | ||||||
|       electron: |       electron: | ||||||
|         specifier: 36.2.1 |         specifier: 36.3.1 | ||||||
|         version: 36.2.1 |         version: 36.3.1 | ||||||
|       prebuild-install: |       prebuild-install: | ||||||
|         specifier: ^7.1.1 |         specifier: ^7.1.1 | ||||||
|         version: 7.1.3 |         version: 7.1.3 | ||||||
| @ -461,8 +461,8 @@ importers: | |||||||
|         specifier: 13.0.0 |         specifier: 13.0.0 | ||||||
|         version: 13.0.0(webpack@5.99.9(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.4)) |         version: 13.0.0(webpack@5.99.9(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.4)) | ||||||
|       electron: |       electron: | ||||||
|         specifier: 36.2.1 |         specifier: 36.3.1 | ||||||
|         version: 36.2.1 |         version: 36.3.1 | ||||||
|       fs-extra: |       fs-extra: | ||||||
|         specifier: 11.3.0 |         specifier: 11.3.0 | ||||||
|         version: 11.3.0 |         version: 11.3.0 | ||||||
| @ -481,7 +481,7 @@ importers: | |||||||
|         version: 7.1.1 |         version: 7.1.1 | ||||||
|       '@electron/remote': |       '@electron/remote': | ||||||
|         specifier: 2.1.2 |         specifier: 2.1.2 | ||||||
|         version: 2.1.2(electron@36.2.1) |         version: 2.1.2(electron@36.3.1) | ||||||
|       '@triliumnext/commons': |       '@triliumnext/commons': | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../packages/commons |         version: link:../../packages/commons | ||||||
| @ -627,8 +627,8 @@ importers: | |||||||
|         specifier: 3.1.10 |         specifier: 3.1.10 | ||||||
|         version: 3.1.10 |         version: 3.1.10 | ||||||
|       electron: |       electron: | ||||||
|         specifier: 36.2.1 |         specifier: 36.3.1 | ||||||
|         version: 36.2.1 |         version: 36.3.1 | ||||||
|       electron-debug: |       electron-debug: | ||||||
|         specifier: 4.1.0 |         specifier: 4.1.0 | ||||||
|         version: 4.1.0 |         version: 4.1.0 | ||||||
| @ -7309,8 +7309,8 @@ packages: | |||||||
|     resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} |     resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} | ||||||
|     engines: {node: '>=8.0.0'} |     engines: {node: '>=8.0.0'} | ||||||
| 
 | 
 | ||||||
|   electron@36.2.1: |   electron@36.3.1: | ||||||
|     resolution: {integrity: sha512-mm1Y+Ms46xcOTA69h8hpqfX392HfV4lga9aEkYkd/Syx1JBStvcACOIouCgGrnZpxNZPVS1jM8NTcMkNjuK6BQ==} |     resolution: {integrity: sha512-LeOZ+tVahmctHaAssLCGRRUa2SAO09GXua3pKdG+WzkbSDMh+3iOPONNVPTqGp8HlWnzGj4r6mhsIbM2RgH+eQ==} | ||||||
|     engines: {node: '>= 12.20.55'} |     engines: {node: '>= 12.20.55'} | ||||||
|     hasBin: true |     hasBin: true | ||||||
| 
 | 
 | ||||||
| @ -15656,9 +15656,9 @@ snapshots: | |||||||
|     transitivePeerDependencies: |     transitivePeerDependencies: | ||||||
|       - supports-color |       - supports-color | ||||||
| 
 | 
 | ||||||
|   '@electron/remote@2.1.2(electron@36.2.1)': |   '@electron/remote@2.1.2(electron@36.3.1)': | ||||||
|     dependencies: |     dependencies: | ||||||
|       electron: 36.2.1 |       electron: 36.3.1 | ||||||
| 
 | 
 | ||||||
|   '@electron/universal@2.0.2': |   '@electron/universal@2.0.2': | ||||||
|     dependencies: |     dependencies: | ||||||
| @ -21262,7 +21262,7 @@ snapshots: | |||||||
|       - supports-color |       - supports-color | ||||||
|     optional: true |     optional: true | ||||||
| 
 | 
 | ||||||
|   electron@36.2.1: |   electron@36.3.1: | ||||||
|     dependencies: |     dependencies: | ||||||
|       '@electron/get': 2.0.3 |       '@electron/get': 2.0.3 | ||||||
|       '@types/node': 22.15.21 |       '@types/node': 22.15.21 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Elian Doran
						Elian Doran