feat(insert time): Add configurable date/time format for Alt+T shortcut

This commit is contained in:
SiriusXT 2025-06-01 15:27:50 +08:00
parent e2ac581b14
commit a8c4b11c9f
8 changed files with 74 additions and 103 deletions

View File

@ -115,6 +115,7 @@ function updateDisplayedShortcuts($container: JQuery<HTMLElement>) {
export default { export default {
updateDisplayedShortcuts, updateDisplayedShortcuts,
setupActionsForElement, setupActionsForElement,
getAction,
getActions, getActions,
getActionsForScope getActionsForScope
}; };

View File

@ -124,22 +124,11 @@ function formatDateISO(date: Date) {
return `${date.getFullYear()}-${padNum(date.getMonth() + 1)}-${padNum(date.getDate())}`; return `${date.getFullYear()}-${padNum(date.getMonth() + 1)}-${padNum(date.getDate())}`;
} }
function formatDateTime(date: Date, userSuppliedFormat?: string): string {
export function formatDateTime(date: Date, userSuppliedFormat?: string): string { if (userSuppliedFormat?.trim()) {
const DEFAULT_FORMAT = 'YYYY-MM-DD HH:mm'; return dayjs(date).format(userSuppliedFormat);
const formatToUse = (typeof userSuppliedFormat === 'string' && userSuppliedFormat.trim() !== "") } else {
? userSuppliedFormat.trim() return `${formatDate(date)} ${formatTime(date)}`;
: DEFAULT_FORMAT;
if (!date) {
date = new Date();
}
try {
return dayjs(date).format(formatToUse);
} catch (e: any) {
console.warn(`TriliumNext: Day.js encountered an error with format string "${formatToUse}". Falling back to default. Error: ${e.message}`);
return dayjs(date).format(DEFAULT_FORMAT);
} }
} }

View File

@ -1431,6 +1431,12 @@
"label": "Automatic read-only size (text notes)", "label": "Automatic read-only size (text notes)",
"unit": "characters" "unit": "characters"
}, },
"custom_date_time_format": {
"title": "Custom Date/Time Format",
"description": "Customize the format of the date and time inserted using the <kbd> </kbd> shortcut. See <a href=\"https://day.js.org/docs/en/display/format\" target=\"_blank\" rel=\"noopener noreferrer\">Day.js docs</a> for available format tokens.",
"format_string": "Format String:",
"formatted_time": "Formatted Time:"
},
"i18n": { "i18n": {
"title": "Localization", "title": "Localization",
"language": "Language", "language": "Language",
@ -1962,16 +1968,5 @@
"title": "Appearance", "title": "Appearance",
"word_wrapping": "Word wrapping", "word_wrapping": "Word wrapping",
"color-scheme": "Color scheme" "color-scheme": "Color scheme"
},
"custom_date_time_format": {
"title": "Custom Date/Time Format (Alt+T)",
"desc1": "Define a custom format for the date and time inserted using the Alt+T shortcut.",
"desc2": "Uses <a href=\"https://day.js.org/docs/en/display/format\" target=\"_blank\" rel=\"noopener noreferrer\">Day.js format tokens</a>. Refer to the Day.js documentation for valid tokens.",
"important_label": "Important:",
"desc3": "If you provide a format string that Day.js does not recognize (e.g., mostly plain text without valid Day.js tokens), the text you typed might be inserted literally. If the format string is left empty, or if Day.js encounters a critical internal error with your format, a default format (e.g., YYYY-MM-DD HH:mm) will be used.",
"format_string_label": "Format String:",
"placeholder": "e.g., DD/MM/YYYY HH:mm:ss or dddd, MMMM D",
"examples_label": "Examples of valid Day.js formats:",
"example_default": "Default-like"
} }
} }

View File

@ -8,6 +8,7 @@ import HeadingStyleOptions from "./options/text_notes/heading_style.js";
import TableOfContentsOptions from "./options/text_notes/table_of_contents.js"; import TableOfContentsOptions from "./options/text_notes/table_of_contents.js";
import HighlightsListOptions from "./options/text_notes/highlights_list.js"; import HighlightsListOptions from "./options/text_notes/highlights_list.js";
import TextAutoReadOnlySizeOptions from "./options/text_notes/text_auto_read_only_size.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 CodeEditorOptions from "./options/code_notes/code_editor.js";
import CodeAutoReadOnlySizeOptions from "./options/code_notes/code_auto_read_only_size.js"; import CodeAutoReadOnlySizeOptions from "./options/code_notes/code_auto_read_only_size.js";
import CodeMimeTypesOptions from "./options/code_notes/code_mime_types.js"; import CodeMimeTypesOptions from "./options/code_notes/code_mime_types.js";
@ -45,7 +46,6 @@ import LanguageOptions from "./options/i18n/language.js";
import type BasicWidget from "../basic_widget.js"; import type BasicWidget from "../basic_widget.js";
import CodeTheme from "./options/code_notes/code_theme.js"; import CodeTheme from "./options/code_notes/code_theme.js";
import RelatedSettings from "./options/related_settings.js"; import RelatedSettings from "./options/related_settings.js";
import DateTimeFormatOptions from "./options/text_notes/date_time_format.js";
const TPL = /*html*/`<div class="note-detail-content-widget note-detail-printable"> const TPL = /*html*/`<div class="note-detail-content-widget note-detail-printable">
<style> <style>
@ -84,13 +84,13 @@ const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", (typeof NoteContextAw
KeyboardShortcutsOptions KeyboardShortcutsOptions
], ],
_optionsTextNotes: [ _optionsTextNotes: [
DateTimeFormatOptions,
EditorOptions, EditorOptions,
HeadingStyleOptions, HeadingStyleOptions,
CodeBlockOptions, CodeBlockOptions,
TableOfContentsOptions, TableOfContentsOptions,
HighlightsListOptions, HighlightsListOptions,
TextAutoReadOnlySizeOptions TextAutoReadOnlySizeOptions,
DateTimeFormatOptions
], ],
_optionsCodeNotes: [ _optionsCodeNotes: [
CodeEditorOptions, CodeEditorOptions,

View File

@ -377,22 +377,8 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
async insertDateTimeToTextCommand() { async insertDateTimeToTextCommand() {
const date = new Date(); const date = new Date();
let userPreferredFormat = ""; const customDateTimeFormat = options.get("customDateTimeFormat");
const dateString = utils.formatDateTime(date, customDateTimeFormat);
try {
await options.initializedPromise;
const customFormatFromSettings = options.get("customDateTimeFormatString");
if (typeof customFormatFromSettings === 'string') {
userPreferredFormat = customFormatFromSettings;
}
} catch (e: any) {
console.error("TriliumNext: Error during options initialization or getting custom date/time format. Using default.", e);
}
const dateString = utils.formatDateTime(date, userPreferredFormat);
this.addTextToEditor(dateString); this.addTextToEditor(dateString);
} }

View File

@ -1,79 +1,79 @@
import OptionsWidget from "../options_widget.js"; import OptionsWidget from "../options_widget.js";
import { t } from "../../../../services/i18n.js"; import { t } from "../../../../services/i18n.js";
import type { OptionMap } from "@triliumnext/commons"; 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*/` const TPL = /*html*/`
<div class="options-section"> <div class="options-section">
<h4>${t("custom_date_time_format.title")}</h4> <h4>${t("custom_date_time_format.title")}</h4>
<p> <p class="description">
${t("custom_date_time_format.desc1")} ${t("custom_date_time_format.description")}
${t("custom_date_time_format.desc2")} </p>
</p>
<p> <table class="table table-borderless">
<strong>${t("custom_date_time_format.important_label")}</strong> <tr>
${t("custom_date_time_format.desc3")} <td>
</p> ${t("custom_date_time_format.format_string")}
</td>
<td>
<input type="text" id="custom-date-time-format" class="form-control custom-date-time-format"
placeholder="YYYY-MM-DD HH:mm">
</td>
</tr>
<tr>
<td>
${t("custom_date_time_format.formatted_time")}
</td>
<td>
<div class="formatted-date" style="padding-left: 0.5rem;">
</div>
</td>
</tr>
</table>
<div class="form-group">
<label for="customDateTimeFormatInput" style="margin-right: 10px;">
${t("custom_date_time_format.format_string_label")}
</label>
<input type="text" id="customDateTimeFormatInput" class="form-control custom-datetime-format-input"
placeholder="${t("custom_date_time_format.placeholder")}"
style="width: 300px; display: inline-block;">
</div>
<p style="margin-top: 5px;">
<em>${t("custom_date_time_format.examples_label")}</em>
<code>YYYY-MM-DD HH:mm</code> (${t("custom_date_time_format.example_default")}),
<code>DD.MM.YYYY</code>,
<code>MMMM D, YYYY h:mm A</code>,
<code>[Today is] dddd</code>
</p>
</div> </div>
`; `;
export default class DateTimeFormatOptions extends OptionsWidget { export default class DateTimeFormatOptions extends OptionsWidget {
private $formatInput!: JQuery<HTMLInputElement>; private $formatInput!: JQuery<HTMLInputElement>;
private $formattedDate!: JQuery<HTMLInputElement>;
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$formatInput = this.$widget.find(
"input.custom-datetime-format-input" this.$formatInput = this.$widget.find("input.custom-date-time-format");
) as JQuery<HTMLInputElement>; this.$formattedDate = this.$widget.find(".formatted-date");
this.$formatInput.on("input", () => { this.$formatInput.on("input", () => {
const formatString = this.$formatInput.val() as string; const dateString = utils.formatDateTime(new Date(), this.$formatInput.val());
this.updateOption("customDateTimeFormatString", formatString); 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; return this.$widget;
} }
async optionsLoaded(options: OptionMap) { async optionsLoaded(options: OptionMap) {
const currentFormat = options.customDateTimeFormatString || ""; 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);
if (this.$formatInput) { const customDateTimeFormat = options.customDateTimeFormat || "YYYY-MM-DD HH:mm";
this.$formatInput.val(currentFormat); this.$formatInput.val(customDateTimeFormat);
} else { const dateString = utils.formatDateTime(new Date(), customDateTimeFormat);
this.$formattedDate.text(dateString);
console.warn(
"TriliumNext DateTimeFormatOptions: $formatInput not initialized when optionsLoaded was called. Attempting to find again."
);
const inputField = this.$widget?.find(
"input.custom-datetime-format-input"
) as JQuery<HTMLInputElement> | undefined;
if (inputField?.length) {
this.$formatInput = inputField;
this.$formatInput.val(currentFormat);
} else {
console.error(
"TriliumNext DateTimeFormatOptions: Could not find format input field in optionsLoaded."
);
}
}
} }
} }

View File

@ -57,6 +57,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
"headingStyle", "headingStyle",
"autoCollapseNoteTree", "autoCollapseNoteTree",
"autoReadonlySizeText", "autoReadonlySizeText",
"customDateTimeFormat",
"autoReadonlySizeCode", "autoReadonlySizeCode",
"overrideThemeFonts", "overrideThemeFonts",
"dailyBackupEnabled", "dailyBackupEnabled",
@ -90,7 +91,6 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
"redirectBareDomain", "redirectBareDomain",
"showLoginInShareTheme", "showLoginInShareTheme",
"splitEditorOrientation", "splitEditorOrientation",
"customDateTimeFormatString",
// AI/LLM integration options // AI/LLM integration options
"aiEnabled", "aiEnabled",

View File

@ -47,7 +47,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
passwordDerivedKeySalt: string; passwordDerivedKeySalt: string;
encryptedDataKey: string; encryptedDataKey: string;
hoistedNoteId: string; hoistedNoteId: string;
customDateTimeFormatString: string; customDateTimeFormat: string;
// Multi-Factor Authentication // Multi-Factor Authentication
mfaEnabled: boolean; mfaEnabled: boolean;