feat: Implement configurable date/time format for Alt+T shortcut

This commit is contained in:
iamvann 2025-05-18 03:14:18 +08:00
parent 56d4d7c20f
commit 4fbfcefa94
6 changed files with 136 additions and 6 deletions

View File

@ -124,8 +124,31 @@ function formatDateISO(date: Date) {
return `${date.getFullYear()}-${padNum(date.getMonth() + 1)}-${padNum(date.getDate())}`;
}
function formatDateTime(date: Date) {
return `${formatDate(date)} ${formatTime(date)}`;
// old version
// function formatDateTime(date: Date) {
// return `${formatDate(date)} ${formatTime(date)}`;
// }
// new version
export function formatDateTime(date: Date, userSuppliedFormat?: string): string {
const DEFAULT_FORMAT = 'YYYY-MM-DD HH:mm';
let formatToUse = DEFAULT_FORMAT;
if (userSuppliedFormat && typeof userSuppliedFormat === 'string' && userSuppliedFormat.trim() !== "") {
formatToUse = userSuppliedFormat.trim();
}
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);
}
}
function localNowDateTime() {

View File

@ -45,6 +45,7 @@ import LanguageOptions from "./options/i18n/language.js";
import type BasicWidget from "../basic_widget.js";
import CodeTheme from "./options/code_notes/code_theme.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">
<style>
@ -83,6 +84,7 @@ const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", (typeof NoteContextAw
KeyboardShortcutsOptions
],
_optionsTextNotes: [
DateTimeFormatOptions,
EditorOptions,
HeadingStyleOptions,
CodeBlockOptions,

View File

@ -267,7 +267,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}
item.on("change:isOpen", () => {
if (!("isOpen" in item) || !item.isOpen ) {
if (!("isOpen" in item) || !item.isOpen) {
return;
}
@ -287,7 +287,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
// Touch bar integration
if (hasTouchBar) {
for (const event of [ "bold", "italic", "underline", "paragraph", "heading" ]) {
for (const event of ["bold", "italic", "underline", "paragraph", "heading"]) {
editor.commands.get(event)?.on("change", () => this.triggerCommand("refreshTouchBar"));
}
}
@ -373,10 +373,33 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
});
}
}
// old version
// insertDateTimeToTextCommand() {
// const date = new Date();
// const dateString = utils.formatDateTime(date);
insertDateTimeToTextCommand() {
// this.addTextToEditor(dateString);
// }
// new version
async insertDateTimeToTextCommand() {
const date = new Date();
const dateString = utils.formatDateTime(date);
let userPreferredFormat = "";
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);
}

View File

@ -0,0 +1,80 @@
import OptionsWidget from "../options_widget.js"; // Path might need adjustment
import { t } from "../../../../services/i18n.js"; // For internationalization, if you want to use it
import type { OptionMap } from "@triliumnext/commons"; // For typing the options object
// Using t() for translatable strings is good practice if the project uses it.
// If not, you can use plain strings.
const TPL = /*html*/`
<div class="options-section">
<h4>${t("options.customDateTimeFormatTitle", "Custom Date/Time Format (Alt+T)")}</h4>
<p>
${t("options.customDateTimeFormatDesc1", "Define a custom format for the date and time inserted using the Alt+T shortcut.")}
${t("options.customDateTimeFormatDesc2", "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.")}
</p>
<p>
<strong>${t("options.customDateTimeFormatImportant", "Important:")}</strong>
${t("options.customDateTimeFormatDesc3", "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.")}
</p>
<div class="form-group">
<label for="customDateTimeFormatInput" style="margin-right: 10px;">
${t("options.customDateTimeFormatLabel", "Format String:")}
</label>
<input type="text" id="customDateTimeFormatInput" class="form-control custom-datetime-format-input"
placeholder="${t("options.customDateTimeFormatPlaceholder", "e.g., DD/MM/YYYY HH:mm:ss or dddd, MMMM D")}"
style="width: 300px; display: inline-block;">
</div>
<p style="margin-top: 5px;">
<em>${t("options.customDateTimeFormatExamplesLabel", "Examples of valid Day.js formats:")}</em>
<code>YYYY-MM-DD HH:mm</code> (${t("options.customDateTimeFormatExampleDefault", "Default-like")}),
<code>DD.MM.YYYY</code>,
<code>MMMM D, YYYY h:mm A</code>,
<code>[Today is] dddd</code>
</p>
</div>
`;
export default class DateTimeFormatOptions extends OptionsWidget {
// Declare class properties with types if needed (jQuery objects are often typed as JQuery<HTMLElement>)
private $formatInput!: JQuery<HTMLInputElement>; // The "!" is a non-null assertion operator
doRender() {
this.$widget = $(TPL); // $ is jQuery, ensure it's available (likely is if OptionsWidget uses it)
this.$formatInput = this.$widget.find(
"input.custom-datetime-format-input"
) as JQuery<HTMLInputElement>; // Type assertion for jQuery result
this.$formatInput.on("input", () => {
const formatString = this.$formatInput.val() as string; // Get value, assert as string
this.updateOption("customDateTimeFormatString", formatString);
});
return this.$widget;
}
async optionsLoaded(options: OptionMap) { // Use the imported OptionMap type
const currentFormat = options.customDateTimeFormatString || "";
if (this.$formatInput) {
this.$formatInput.val(currentFormat);
} else {
// Fallback logic as before, ensure $widget is available if $formatInput isn't yet
console.warn(
"TriliumNext DateTimeFormatOptions: $formatInput not initialized when optionsLoaded was called. Attempting to find again."
);
const inputField = this.$widget?.find( // Optional chaining for $widget
"input.custom-datetime-format-input"
) as JQuery<HTMLInputElement> | undefined; // Result could be undefined
if (inputField?.length) { // Optional chaining and check length
this.$formatInput = inputField;
this.$formatInput.val(currentFormat);
} else {
console.error(
"TriliumNext DateTimeFormatOptions: Could not find format input field in optionsLoaded."
);
}
}
}
}

View File

@ -84,6 +84,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
"redirectBareDomain",
"showLoginInShareTheme",
"splitEditorOrientation",
"customDateTimeFormatString",
// AI/LLM integration options
"aiEnabled",

View File

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