diff --git a/apps/client/src/services/keyboard_actions.ts b/apps/client/src/services/keyboard_actions.ts index dfa888620..3cb0ffd33 100644 --- a/apps/client/src/services/keyboard_actions.ts +++ b/apps/client/src/services/keyboard_actions.ts @@ -115,6 +115,7 @@ function updateDisplayedShortcuts($container: JQuery) { export default { updateDisplayedShortcuts, setupActionsForElement, + getAction, getActions, getActionsForScope }; diff --git a/apps/client/src/services/utils.ts b/apps/client/src/services/utils.ts index ab6e45847..590c596b2 100644 --- a/apps/client/src/services/utils.ts +++ b/apps/client/src/services/utils.ts @@ -124,8 +124,12 @@ function formatDateISO(date: Date) { return `${date.getFullYear()}-${padNum(date.getMonth() + 1)}-${padNum(date.getDate())}`; } -function formatDateTime(date: Date) { - return `${formatDate(date)} ${formatTime(date)}`; +function formatDateTime(date: Date, userSuppliedFormat?: string): string { + if (userSuppliedFormat?.trim()) { + return dayjs(date).format(userSuppliedFormat); + } else { + return `${formatDate(date)} ${formatTime(date)}`; + } } function localNowDateTime() { diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 4d39265d1..13ee699d7 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1431,6 +1431,12 @@ "label": "Automatic read-only size (text notes)", "unit": "characters" }, + "custom_date_time_format": { + "title": "Custom Date/Time Format", + "description": "Customize the format of the date and time inserted via or the toolbar. See Day.js docs for available format tokens.", + "format_string": "Format string:", + "formatted_time": "Formatted date/time:" + }, "i18n": { "title": "Localization", "language": "Language", diff --git a/apps/client/src/widgets/type_widgets/ckeditor/config.ts b/apps/client/src/widgets/type_widgets/ckeditor/config.ts index bd74d0512..210fac3bf 100644 --- a/apps/client/src/widgets/type_widgets/ckeditor/config.ts +++ b/apps/client/src/widgets/type_widgets/ckeditor/config.ts @@ -189,7 +189,7 @@ export function buildClassicToolbar(multilineToolbar: boolean) { { label: "Insert", icon: "plus", - items: ["imageUpload", "|", "link", "bookmark", "internallink", "includeNote", "|", "specialCharacters", "emoji", "math", "mermaid", "horizontalLine", "pageBreak"] + items: ["imageUpload", "|", "link", "bookmark", "internallink", "includeNote", "|", "specialCharacters", "emoji", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"] }, "|", "outdent", @@ -222,6 +222,7 @@ export function buildFloatingToolbar() { "|", "code", "link", + "bookmark", "removeFormat", "internallink", "cuttonote" @@ -243,7 +244,7 @@ export function buildFloatingToolbar() { { label: "Insert", icon: "plus", - items: ["bookmark", "internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak"] + items: ["bookmark", "internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"] }, "|", "outdent", diff --git a/apps/client/src/widgets/type_widgets/content_widget.ts b/apps/client/src/widgets/type_widgets/content_widget.ts index 00c30f5f7..614159cb7 100644 --- a/apps/client/src/widgets/type_widgets/content_widget.ts +++ b/apps/client/src/widgets/type_widgets/content_widget.ts @@ -8,6 +8,7 @@ import HeadingStyleOptions from "./options/text_notes/heading_style.js"; import TableOfContentsOptions from "./options/text_notes/table_of_contents.js"; import HighlightsListOptions from "./options/text_notes/highlights_list.js"; import TextAutoReadOnlySizeOptions from "./options/text_notes/text_auto_read_only_size.js"; +import DateTimeFormatOptions from "./options/text_notes/date_time_format.js"; import CodeEditorOptions from "./options/code_notes/code_editor.js"; import CodeAutoReadOnlySizeOptions from "./options/code_notes/code_auto_read_only_size.js"; import CodeMimeTypesOptions from "./options/code_notes/code_mime_types.js"; @@ -88,7 +89,8 @@ const CONTENT_WIDGETS: Record { - if (!("isOpen" in item) || !item.isOpen ) { + if (!("isOpen" in item) || !item.isOpen) { return; } @@ -375,9 +375,10 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { } } - insertDateTimeToTextCommand() { + insertDateTimeToTextCommand() { const date = new Date(); - const dateString = utils.formatDateTime(date); + const customDateTimeFormat = options.get("customDateTimeFormat"); + const dateString = utils.formatDateTime(date, customDateTimeFormat); this.addTextToEditor(dateString); } diff --git a/apps/client/src/widgets/type_widgets/options/text_notes/date_time_format.ts b/apps/client/src/widgets/type_widgets/options/text_notes/date_time_format.ts new file mode 100644 index 000000000..5728ac8b4 --- /dev/null +++ b/apps/client/src/widgets/type_widgets/options/text_notes/date_time_format.ts @@ -0,0 +1,67 @@ +import OptionsWidget from "../options_widget.js"; +import { t } from "../../../../services/i18n.js"; +import type { OptionMap } from "@triliumnext/commons"; +import utils from "../../../../services/utils.js"; +import keyboardActionsService from "../../../../services/keyboard_actions.js"; +import linkService from "../../../.././services/link.js"; + +const TPL = /*html*/` +
+

${t("custom_date_time_format.title")}

+ +

+ ${t("custom_date_time_format.description")} +

+ +
+
+ + +
+
+ +
+
+
+
+`; + +export default class DateTimeFormatOptions extends OptionsWidget { + + private $formatInput!: JQuery; + private $formattedDate!: JQuery; + + doRender() { + this.$widget = $(TPL); + + this.$formatInput = this.$widget.find("input.custom-date-time-format"); + this.$formattedDate = this.$widget.find(".formatted-date"); + + this.$formatInput.on("input", () => { + const dateString = utils.formatDateTime(new Date(), this.$formatInput.val()); + this.$formattedDate.text(dateString); + }); + + this.$formatInput.on('blur keydown', (e) => { + if (e.type === 'blur' || (e.type === 'keydown' && e.key === 'Enter')) { + this.updateOption("customDateTimeFormat", this.$formatInput.val()); + } + }); + + return this.$widget; + } + + async optionsLoaded(options: OptionMap) { + const shortcutKey = (await keyboardActionsService.getAction("insertDateTimeToText")).effectiveShortcuts.join(", "); + const $link = await linkService.createLink("_hidden/_options/_optionsShortcuts", { + "title": shortcutKey, + "showTooltip": false + }); + this.$widget.find(".description").find("kbd").replaceWith($link); + + const customDateTimeFormat = options.customDateTimeFormat || "YYYY-MM-DD HH:mm"; + this.$formatInput.val(customDateTimeFormat); + const dateString = utils.formatDateTime(new Date(), customDateTimeFormat); + this.$formattedDate.text(dateString); + } +} diff --git a/apps/server/src/routes/api/options.ts b/apps/server/src/routes/api/options.ts index c69f7568f..42d4fb110 100644 --- a/apps/server/src/routes/api/options.ts +++ b/apps/server/src/routes/api/options.ts @@ -57,6 +57,7 @@ const ALLOWED_OPTIONS = new Set([ "headingStyle", "autoCollapseNoteTree", "autoReadonlySizeText", + "customDateTimeFormat", "autoReadonlySizeCode", "overrideThemeFonts", "dailyBackupEnabled", diff --git a/packages/ckeditor5/src/icons/date-time.svg b/packages/ckeditor5/src/icons/date-time.svg new file mode 100644 index 000000000..8e8907da6 --- /dev/null +++ b/packages/ckeditor5/src/icons/date-time.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/packages/ckeditor5/src/plugins.ts b/packages/ckeditor5/src/plugins.ts index 39932ef6d..3310493c0 100644 --- a/packages/ckeditor5/src/plugins.ts +++ b/packages/ckeditor5/src/plugins.ts @@ -5,6 +5,7 @@ import UploadimagePlugin from "./plugins/uploadimage.js"; import ItalicAsEmPlugin from "./plugins/italic_as_em.js"; import StrikethroughAsDel from "./plugins/strikethrough_as_del.js"; import InternalLinkPlugin from "./plugins/internallink.js"; +import InsertDateTimePlugin from "./plugins/insert_date_time.js"; import ReferenceLink from "./plugins/referencelink.js"; import RemoveFormatLinksPlugin from "./plugins/remove_format_links.js"; import IndentBlockShortcutPlugin from "./plugins/indent_block_shortcut.js"; @@ -36,6 +37,7 @@ const TRILIUM_PLUGINS: typeof Plugin[] = [ ItalicAsEmPlugin, StrikethroughAsDel, InternalLinkPlugin, + InsertDateTimePlugin, RemoveFormatLinksPlugin, IndentBlockShortcutPlugin, MarkdownImportPlugin, diff --git a/packages/ckeditor5/src/plugins/insert_date_time.ts b/packages/ckeditor5/src/plugins/insert_date_time.ts new file mode 100644 index 000000000..e1417dbe7 --- /dev/null +++ b/packages/ckeditor5/src/plugins/insert_date_time.ts @@ -0,0 +1,31 @@ +import { ButtonView, Plugin } from 'ckeditor5'; +import dateTimeIcon from '../icons/date-time.svg?raw'; + +export default class InsertDateTimePlugin extends Plugin { + init() { + const editor = this.editor; + + editor.ui.componentFactory.add('dateTime', locale => { + const view = new ButtonView( locale ); + + view.set( { + label: 'Date time', + icon: dateTimeIcon, + tooltip: true + } ); + + // enable internal link only if the editor is not read only + view.bind('isEnabled').to(editor, 'isReadOnly', isReadOnly => !isReadOnly); + + view.on('execute', () => { + const editorEl = editor.editing.view.getDomRoot(); + const component = glob.getComponentByEl(editorEl); + + component.triggerCommand('insertDateTimeToText'); + editor.editing.view.focus(); + } ); + + return view; + }); + } +} \ No newline at end of file diff --git a/packages/commons/src/lib/options_interface.ts b/packages/commons/src/lib/options_interface.ts index 19125d125..32f731936 100644 --- a/packages/commons/src/lib/options_interface.ts +++ b/packages/commons/src/lib/options_interface.ts @@ -47,6 +47,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions