mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 18:12:29 +08:00
feat(client): add a copy button to read-only text
This commit is contained in:
parent
4752db6bc5
commit
02e2b5d4ad
@ -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) {
|
||||||
|
@ -6,16 +6,16 @@ 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) {
|
||||||
const normalizedMimeType = extractLanguageFromClassList(codeBlock);
|
const normalizedMimeType = extractLanguageFromClassList(codeBlock);
|
||||||
@ -23,10 +23,20 @@ export async function applySyntaxHighlight($container: JQuery<HTMLElement>) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
applySingleBlockSyntaxHighlight($(codeBlock), normalizedMimeType);
|
applyCopyToClipboardButton($(codeBlock));
|
||||||
|
|
||||||
|
if (syntaxHighlightingEnabled) {
|
||||||
|
applySingleBlockSyntaxHighlight($(codeBlock), normalizedMimeType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyCopyToClipboardButton($codeBlock: JQuery<HTMLElement>) {
|
||||||
|
const $copyButton = $("<button>")
|
||||||
|
.addClass("bx component btn tn-tool-button bx-copy copy-button");
|
||||||
|
$codeBlock.parent().append($copyButton);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies syntax highlight to the given code block (assumed to be <pre><code>), using highlight.js.
|
* Applies syntax highlight to the given code block (assumed to be <pre><code>), using highlight.js.
|
||||||
*/
|
*/
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user