Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions src/public/app/services/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
2 changes: 2 additions & 0 deletions src/public/app/widgets/type_widgets/content_widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -66,6 +67,7 @@ const CONTENT_WIDGETS = {
HeadingStyleOptions,
TableOfContentsOptions,
HighlightsListOptions,
DateTimeFormatOptions,
TextAutoReadOnlySizeOptions
],
_optionsCodeNotes: [
Expand Down
54 changes: 37 additions & 17 deletions src/public/app/widgets/type_widgets/editable_text.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -211,7 +212,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
this.watchdog?.editor.editing.view.focus();
}

show() {}
show() { }

getEditor() {
return this.watchdog?.editor;
Expand All @@ -225,10 +226,29 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}
}

insertDateTimeToTextCommand() {
// old version
// insertDateTimeToTextCommand() {
// const date = new Date();
// const dateString = utils.formatDateTime(date);

// this.addTextToEditor(dateString);
// }

// new version
async insertDateTimeToTextCommand() {
const date = new Date();
const dateString = utils.formatDateTime(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);
}

Expand All @@ -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);
});
}

Expand All @@ -250,7 +270,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
});
}

addTextToActiveEditorEvent({text}) {
addTextToActiveEditorEvent({ text }) {
if (!this.isActive()) {
return;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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() {
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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."
);
}
}
}
}
3 changes: 2 additions & 1 deletion src/routes/api/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ const ALLOWED_OPTIONS = new Set([
'customSearchEngineName',
'customSearchEngineUrl',
'promotedAttributesOpenInRibbon',
'editedNotesOpenInRibbon'
'editedNotesOpenInRibbon',
'customDateTimeFormatString'
]);

function getOptions() {
Expand Down