Merge 2844331f1043137d57e0ada0441e89689900dbbc into 82a437f2a83fc10299f3205713f7dcb04e2d047c

This commit is contained in:
vanndoublen 2025-05-17 01:56:58 +08:00 committed by GitHub
commit 26e3e6186f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 145 additions and 21 deletions

View File

@ -73,8 +73,34 @@ function formatDateISO(date) {
return `${date.getFullYear()}-${padNum(date.getMonth() + 1)}-${padNum(date.getDate())}`;
}
function formatDateTime(date) {
return `${formatDate(date)} ${formatTime(date)}`;
// old version
// function formatDateTime(date) {
// return `${formatDate(date)} ${formatTime(date)}`;
// }
// In utils.js
// import dayjs from 'dayjs'; // Assuming dayjs is available in this scope
// new version
function formatDateTime(date, userSuppliedFormat) {
let formatToUse;
if (userSuppliedFormat && typeof userSuppliedFormat === 'string' && userSuppliedFormat.trim() !== "") {
formatToUse = userSuppliedFormat.trim();
} else {
formatToUse = 'YYYY-MM-DD HH:mm'; // Trilium's default format
}
if (!date) {
date = new Date();
}
try {
return dayjs(date).format(formatToUse);
} catch (e) {
console.warn(`Day.js: Invalid format string "${formatToUse}". Falling back. Error:`, e.message);
return dayjs(date).format('YYYY-MM-DD HH:mm');
}
}
function localNowDateTime() {

View File

@ -8,6 +8,7 @@ import KeyboardShortcutsOptions from "./options/shortcuts.js";
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 DateTimeFormatOptions from "./options/text_notes/date_time_format.js";
import TextAutoReadOnlySizeOptions from "./options/text_notes/text_auto_read_only_size.js";
import VimKeyBindingsOptions from "./options/code_notes/vim_key_bindings.js";
import WrapLinesOptions from "./options/code_notes/wrap_lines.js";
@ -66,6 +67,7 @@ const CONTENT_WIDGETS = {
HeadingStyleOptions,
TableOfContentsOptions,
HighlightsListOptions,
DateTimeFormatOptions,
TextAutoReadOnlySizeOptions
],
_optionsCodeNotes: [

View File

@ -9,6 +9,7 @@ import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
import link from "../../services/link.js";
import appContext from "../../components/app_context.js";
import dialogService from "../../services/dialog.js";
import server from '../../services/server.js';
const ENABLE_INSPECTOR = false;
@ -109,9 +110,9 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
(await mimeTypesService.getMimeTypes())
.filter(mt => mt.enabled)
.map(mt => ({
language: mt.mime.toLowerCase().replace(/[\W_]+/g,"-"),
label: mt.title
}));
language: mt.mime.toLowerCase().replace(/[\W_]+/g, "-"),
label: mt.title
}));
// CKEditor since version 12 needs the element to be visible before initialization. At the same time,
// we want to avoid flicker - i.e., show editor only once everything is ready. That's why we have separate
@ -211,7 +212,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
this.watchdog?.editor.editing.view.focus();
}
show() {}
show() { }
getEditor() {
return this.watchdog?.editor;
@ -225,10 +226,29 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}
}
insertDateTimeToTextCommand() {
const date = new Date();
const dateString = utils.formatDateTime(date);
// old version
// insertDateTimeToTextCommand() {
// const date = new Date();
// const dateString = utils.formatDateTime(date);
// this.addTextToEditor(dateString);
// }
// new version
async insertDateTimeToTextCommand() {
const date = new Date();
let userPreferredFormat = ""; //Default
try {
const allOptions = await server.get('options');
if (allOptions && typeof allOptions.customDateTimeFormatString === 'string') {
userPreferredFormat = allOptions.customDateTimeFormatString;
}
} catch (e) {
console.error("Trilium: Failed to fetch options for custom date/time format. Using default.", e);
}
const dateString = utils.formatDateTime(date, userPreferredFormat);
this.addTextToEditor(dateString);
}
@ -237,7 +257,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
this.watchdog.editor.model.change(writer => {
const insertPosition = this.watchdog.editor.model.document.selection.getFirstPosition();
writer.insertText(linkTitle, {linkHref: linkHref}, insertPosition);
writer.insertText(linkTitle, { linkHref: linkHref }, insertPosition);
});
}
@ -250,7 +270,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
});
}
addTextToActiveEditorEvent({text}) {
addTextToActiveEditorEvent({ text }) {
if (!this.isActive()) {
return;
}
@ -283,7 +303,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
return !selection.isCollapsed;
}
async executeWithTextEditorEvent({callback, resolve, ntxId}) {
async executeWithTextEditorEvent({ callback, resolve, ntxId }) {
if (!this.isNoteContext(ntxId)) {
return;
}
@ -300,7 +320,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
addLinkToTextCommand() {
const selectedText = this.getSelectedText();
this.triggerCommand('showAddLinkDialog', {textTypeWidget: this, text: selectedText})
this.triggerCommand('showAddLinkDialog', { textTypeWidget: this, text: selectedText })
}
getSelectedText() {
@ -347,29 +367,29 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}
addIncludeNoteToTextCommand() {
this.triggerCommand("showIncludeNoteDialog", {textTypeWidget: this});
this.triggerCommand("showIncludeNoteDialog", { textTypeWidget: this });
}
addIncludeNote(noteId, boxSize) {
this.watchdog.editor.model.change( writer => {
this.watchdog.editor.model.change(writer => {
// Insert <includeNote>*</includeNote> at the current selection position
// in a way that will result in creating a valid model structure
this.watchdog.editor.model.insertContent(writer.createElement('includeNote', {
noteId: noteId,
boxSize: boxSize
}));
} );
});
}
async addImage(noteId) {
const note = await froca.getNote(noteId);
this.watchdog.editor.model.change( writer => {
this.watchdog.editor.model.change(writer => {
const encodedTitle = encodeURIComponent(note.title);
const src = `api/images/${note.noteId}/${encodedTitle}`;
this.watchdog.editor.execute( 'insertImage', { source: src } );
} );
this.watchdog.editor.execute('insertImage', { source: src });
});
}
async createNoteForReferenceLink(title) {
@ -385,7 +405,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
return resp.note.getBestNotePathString();
}
async refreshIncludedNoteEvent({noteId}) {
async refreshIncludedNoteEvent({ noteId }) {
this.refreshIncludedNote(this.$editor, noteId);
}
}

View File

@ -0,0 +1,75 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Custom Date/Time Format (Alt+T)</h4>
<p>
Define a custom format for the date and time inserted using the Alt+T shortcut.
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>Important:</strong> If you provide a format string that Day.js does not recognize
(e.g., mostly plain text without valid <a href="https://day.js.org/docs/en/display/format" target="_blank" rel="noopener noreferrer">Day.js tokens</a>),
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;">Format String:</label>
<input type="text" id="customDateTimeFormatInput" class="form-control custom-datetime-format-input" placeholder="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>Examples of valid Day.js formats:</em>
<code>YYYY-MM-DD HH:mm</code> (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 {
doRender() {
this.$widget = $(TPL);
this.$formatInput = this.$widget.find(
"input.custom-datetime-format-input"
);
//listen to input
this.$formatInput.on("input", () => {
const formatString = this.$formatInput.val();
this.updateOption("customDateTimeFormatString", formatString);
});
return this.$widget; //render method to return the widget
}
async optionsLoaded(options) {
const currentFormat = options.customDateTimeFormatString || "";
if (this.$formatInput) {
this.$formatInput.val(currentFormat);
} else {
console.warn(
"DateTimeFormatOptions: $formatInput not initialized when optionsLoaded was called. Attempting to find again."
);
const inputField = this.$widget.find(
"input.custom-datetime-format-input"
);
if (inputField.length) {
this.$formatInput = inputField;
this.$formatInput.val(currentFormat);
} else {
console.error(
"DateTimeFormatOptions: Could not find format input field in optionsLoaded."
);
}
}
}
}

View File

@ -57,7 +57,8 @@ const ALLOWED_OPTIONS = new Set([
'customSearchEngineName',
'customSearchEngineUrl',
'promotedAttributesOpenInRibbon',
'editedNotesOpenInRibbon'
'editedNotesOpenInRibbon',
'customDateTimeFormatString'
]);
function getOptions() {