feat(ckeditor/watchdog): functional copy to clipboard button

This commit is contained in:
Elian Doran 2025-12-07 21:21:55 +02:00
parent 75a1fcc933
commit 397fb785d6
No known key found for this signature in database
7 changed files with 58 additions and 16 deletions

View file

@ -205,7 +205,8 @@
"info": {
"modalTitle": "Info message",
"closeButton": "Close",
"okButton": "OK"
"okButton": "OK",
"copy_to_clipboard": "Copy to clipboard"
},
"jump_to_note": {
"search_placeholder": "Search for note by its name or type > for commands...",

View file

@ -8,11 +8,19 @@ import { useTriliumEvent } from "../react/hooks";
import { isValidElement } from "preact";
import { ConfirmWithMessageOptions } from "./confirm";
import "./info.css";
import server from "../../services/server";
import { ToMarkdownResponse } from "@triliumnext/commons";
import { copyTextWithToast } from "../../services/clipboard_ext";
export interface InfoExtraProps extends Partial<Pick<ModalProps, "size" | "title">> {
/** Adds a button in the footer that allows easily copying the content of the infobox to clipboard. */
copyToClipboardButton?: boolean;
}
export type InfoExtraProps = Partial<Pick<ModalProps, "size" | "title">>;
export type InfoProps = ConfirmWithMessageOptions & InfoExtraProps;
export default function InfoDialog() {
const modalRef = useRef<HTMLDivElement>(null);
const [ opts, setOpts ] = useState<EventData<"showInfoDialog">>();
const [ shown, setShown ] = useState(false);
const okButtonRef = useRef<HTMLButtonElement>(null);
@ -31,11 +39,28 @@ export default function InfoDialog() {
setShown(false);
}}
onShown={() => okButtonRef.current?.focus?.()}
footer={<Button
buttonRef={okButtonRef}
text={t("info.okButton")}
onClick={() => setShown(false)}
/>}
modalRef={modalRef}
footer={<>
{opts?.copyToClipboardButton && (
<Button
text={t("info.copy_to_clipboard")}
icon="bx bx-copy"
onClick={async () => {
const htmlContent = modalRef.current?.querySelector<HTMLDivElement>(".modal-body")?.innerHTML;
if (!htmlContent) return;
const { markdownContent } = await server.post<ToMarkdownResponse>("other/to-markdown", { htmlContent });
copyTextWithToast(markdownContent);
}}
/>
)}
<Button
buttonRef={okButtonRef}
text={t("info.okButton")}
onClick={() => setShown(false)}
/>
</>}
show={shown}
stackable
scrollable

View file

@ -7,15 +7,12 @@ import Modal from "../react/Modal";
import Button from "../react/Button";
import { useTriliumEvent } from "../react/hooks";
import { CKEditorApi } from "../type_widgets/text/CKEditorWithWatchdog";
import { RenderMarkdownResponse } from "@triliumnext/commons";
export interface MarkdownImportOpts {
editorApi: CKEditorApi;
}
interface RenderMarkdownResponse {
htmlContent: string;
}
export default function MarkdownImportDialog() {
const markdownImportTextArea = useRef<HTMLTextAreaElement>(null);
const editorApiRef = useRef<CKEditorApi>(null);

View file

@ -310,10 +310,11 @@ function useWatchdogCrashHandling() {
dialog.info(<>
<p>{t("editable_text.editor_crashed_details_intro")}</p>
<h3>{t("editable_text.editor_crashed_details_title")}</h3>
<pre>{formattedCrash}</pre>
<pre><code class="language-application-json">{formattedCrash}</code></pre>
</>, {
title: t("editable_text.editor_crashed_title"),
size: "lg"
size: "lg",
copyToClipboardButton: true
});
}
}

View file

@ -2,6 +2,8 @@ import type { Request } from "express";
import becca from "../../becca/becca.js";
import markdownService from "../../services/import/markdown.js";
import markdown from "../../services/export/markdown.js";
import { RenderMarkdownResponse, ToMarkdownResponse } from "@triliumnext/commons";
function getIconUsage() {
const iconClassToCountMap: Record<string, number> = {};
@ -29,13 +31,20 @@ function getIconUsage() {
function renderMarkdown(req: Request) {
const { markdownContent } = req.body;
return {
htmlContent: markdownService.renderToHtml(markdownContent, "")
};
} satisfies RenderMarkdownResponse;
}
function toMarkdown(req: Request) {
const { htmlContent } = req.body;
return {
markdownContent: markdown.toMarkdown(htmlContent)
} satisfies ToMarkdownResponse;
}
export default {
getIconUsage,
renderMarkdown
renderMarkdown,
toMarkdown
};

View file

@ -348,6 +348,7 @@ function register(app: express.Application) {
route(GET, "/api/fonts", [auth.checkApiAuthOrElectron], fontsRoute.getFontCss);
apiRoute(GET, "/api/other/icon-usage", otherRoute.getIconUsage);
apiRoute(PST, "/api/other/render-markdown", otherRoute.renderMarkdown);
apiRoute(PST, "/api/other/to-markdown", otherRoute.toMarkdown);
apiRoute(GET, "/api/recent-changes/:ancestorNoteId", recentChangesApiRoute.getRecentChanges);
apiRoute(GET, "/api/edited-notes/:date", revisionsApiRoute.getEditedNotesOnDate);

View file

@ -277,3 +277,11 @@ export interface NoteMapPostResponse {
export interface UpdateAttributeResponse {
attributeId: string;
}
export interface RenderMarkdownResponse {
htmlContent: string;
}
export interface ToMarkdownResponse {
markdownContent: string;
}