Merge branch 'Highlighted-Text' of https://github.com/SiriusXT/trilium into Highlighted-Text

This commit is contained in:
SiriusXT 2023-06-04 16:02:32 +08:00
commit 5b421d51b5
12 changed files with 160 additions and 29 deletions

View file

@ -40,19 +40,25 @@ function register(router) {
}
});
const ALLOWED_PROPERTIES_FOR_PATCH = {
const ALLOWED_PROPERTIES_FOR_PATCH_LABEL = {
'value': [v.notNull, v.isString],
'position': [v.notNull, v.isInteger]
};
const ALLOWED_PROPERTIES_FOR_PATCH_RELATION = {
'position': [v.notNull, v.isInteger]
};
eu.route(router, 'patch' ,'/etapi/attributes/:attributeId', (req, res, next) => {
const attribute = eu.getAndCheckAttribute(req.params.attributeId);
if (attribute.type === 'relation') {
if (attribute.type === 'label') {
eu.validateAndPatch(attribute, req.body, ALLOWED_PROPERTIES_FOR_PATCH_LABEL);
} else if (attribute.type === 'relation') {
eu.getAndCheckNote(req.body.value);
}
eu.validateAndPatch(attribute, req.body, ALLOWED_PROPERTIES_FOR_PATCH);
eu.validateAndPatch(attribute, req.body, ALLOWED_PROPERTIES_FOR_PATCH_RELATION);
}
attribute.save();

View file

@ -374,7 +374,7 @@ paths:
schema:
$ref: '#/components/schemas/Error'
patch:
description: patch a branch identified by the branchId with changes in the body
description: patch a branch identified by the branchId with changes in the body. Only prefix and notePosition can be updated. If you want to update other properties, you need to delete the old branch and create a new one.
operationId: patchBranchById
requestBody:
required: true
@ -456,7 +456,7 @@ paths:
schema:
$ref: '#/components/schemas/Error'
patch:
description: patch a attribute identified by the attributeId with changes in the body
description: patch a attribute identified by the attributeId with changes in the body. For labels, only value and position can be updated. For relations, only position can be updated. If you want to modify other properties, you need to delete the old attribute and create a new one.
operationId: patchAttributeById
requestBody:
required: true

View file

@ -451,16 +451,23 @@ export default class TabManager extends Component {
this.tabsUpdate.scheduleUpdate();
}
noteContextReorderEvent({ntxIdsInOrder}) {
const order = {};
let i = 0;
for (const ntxId of ntxIdsInOrder) {
order[ntxId] = i++;
}
noteContextReorderEvent({ntxIdsInOrder, oldMainNtxId, newMainNtxId}) {
const order = Object.fromEntries(ntxIdsInOrder.map((v, i) => [v, i]));
this.children.sort((a, b) => order[a.ntxId] < order[b.ntxId] ? -1 : 1);
if (oldMainNtxId && newMainNtxId) {
this.children.forEach(c => {
if (c.ntxId === newMainNtxId) {
// new main context has null mainNtxId
c.mainNtxId = null;
} else if (c.ntxId === oldMainNtxId || c.mainNtxId === oldMainNtxId) {
// old main context or subcontexts all have the new mainNtxId
c.mainNtxId = newMainNtxId;
}
});
}
this.tabsUpdate.scheduleUpdate();
}

View file

@ -76,6 +76,7 @@ import CodeButtonsWidget from "../widgets/floating_buttons/code_buttons.js";
import ApiLogWidget from "../widgets/api_log.js";
import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js";
import ScriptExecutorWidget from "../widgets/ribbon_widgets/script_executor.js";
import MovePaneButton from "../widgets/buttons/move_pane_button.js";
export default class DesktopLayout {
constructor(customWidgets) {
@ -124,6 +125,8 @@ export default class DesktopLayout {
.child(new NoteIconWidget())
.child(new NoteTitleWidget())
.child(new SpacerWidget(0, 1))
.child(new MovePaneButton(true))
.child(new MovePaneButton(false))
.child(new ClosePaneButton())
.child(new CreatePaneButton())
)

View file

@ -483,6 +483,13 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
*/
this.randomString = utils.randomString;
/**
* @method
* @param {int} size in bytes
* @return {string} formatted string
*/
this.formatNoteSize = utils.formatNoteSize;
this.logMessages = {};
this.logSpacedUpdates = {};

View file

@ -354,6 +354,17 @@ function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
function formatNoteSize(size) {
size = Math.max(Math.round(size / 1024), 1);
if (size < 1024) {
return `${size} KiB`;
}
else {
return `${Math.round(size / 102.4) / 10} MiB`;
}
}
export default {
reloadFrontendApp,
parseDate,
@ -396,5 +407,6 @@ export default {
filterAttributeName,
isValidAttributeName,
sleep,
escapeRegExp
escapeRegExp,
formatNoteSize
};

View file

@ -7,6 +7,10 @@ export default class ClosePaneButton extends OnClickButtonWidget {
&& this.noteContext && !!this.noteContext.mainNtxId;
}
async noteContextReorderEvent({ntxIdsInOrder}) {
this.refresh();
}
constructor() {
super();

View file

@ -0,0 +1,47 @@
import OnClickButtonWidget from "./onclick_button.js";
import appContext from "../../components/app_context.js";
export default class MovePaneButton extends OnClickButtonWidget {
constructor(isMovingLeft) {
super();
this.isMovingLeft = isMovingLeft;
this.icon(isMovingLeft ? "bx-chevron-left" : "bx-chevron-right")
.title(isMovingLeft ? "Move left" : "Move right")
.titlePlacement("bottom")
.onClick(async (widget, e) => {
e.stopPropagation();
widget.triggerCommand("moveThisNoteSplit", {ntxId: widget.getClosestNtxId(), isMovingLeft: this.isMovingLeft});
})
.class("icon-action");
}
isEnabled() {
if (!super.isEnabled()) {
return false;
}
if (this.isMovingLeft) {
// movable if the current context is not a main context, i.e. non-null mainNtxId
return !!this.noteContext?.mainNtxId;
} else {
const currentIndex = appContext.tabManager.noteContexts.findIndex(c => c.ntxId === this.ntxId);
const nextContext = appContext.tabManager.noteContexts[currentIndex + 1];
// movable if the next context is not null and not a main context, i.e. non-null mainNtxId
return !!nextContext?.mainNtxId;
}
}
async noteContextRemovedEvent() {
this.refresh();
}
async newNoteContextCreatedEvent() {
this.refresh();
}
async noteContextReorderEvent() {
this.refresh();
}
}

View file

@ -74,6 +74,50 @@ export default class SplitNoteContainer extends FlexContainer {
appContext.tabManager.removeNoteContext(ntxId);
}
async moveThisNoteSplitCommand({ntxId, isMovingLeft}) {
if (!ntxId) {
logError("empty ntxId!");
return;
}
const contexts = appContext.tabManager.noteContexts;
const currentIndex = contexts.findIndex(c => c.ntxId === ntxId);
const leftIndex = isMovingLeft ? currentIndex - 1 : currentIndex;
if (currentIndex === -1 || leftIndex < 0 || leftIndex + 1 >= contexts.length) {
logError(`invalid context! currentIndex: ${currentIndex}, leftIndex: ${leftIndex}, contexts.length: ${contexts.length}`);
return;
}
if (contexts[leftIndex].isEmpty() && contexts[leftIndex + 1].isEmpty()) {
// no op
return;
}
const ntxIds = contexts.map(c => c.ntxId);
const newNtxIds = [
...ntxIds.slice(0, leftIndex),
ntxIds[leftIndex + 1],
ntxIds[leftIndex],
...ntxIds.slice(leftIndex + 2),
];
const isChangingMainContext = !contexts[leftIndex].mainNtxId;
this.triggerCommand("noteContextReorder", {
ntxIdsInOrder: newNtxIds,
oldMainNtxId: isChangingMainContext ? ntxIds[leftIndex] : null,
newMainNtxId: isChangingMainContext ? ntxIds[leftIndex + 1]: null,
});
// reorder the note context widgets
this.$widget.find(`[data-ntx-id="${ntxIds[leftIndex]}"]`)
.insertAfter(this.$widget.find(`[data-ntx-id="${ntxIds[leftIndex + 1]}"]`));
// activate context that now contains the original note
await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]);
}
activeContextChangedEvent() {
this.refresh();
}

View file

@ -136,7 +136,7 @@ export default class FilePropertiesWidget extends NoteContextAwareWidget {
const noteComplement = await this.noteContext.getNoteComplement();
this.$fileSize.text(`${noteComplement.contentLength} bytes`);
this.$fileSize.text(utils.formatNoteSize(noteComplement.contentLength));
// open doesn't work for protected notes since it works through browser which isn't in protected session
this.$openButton.toggle(!note.isProtected);

View file

@ -1,5 +1,6 @@
import NoteContextAwareWidget from "../note_context_aware_widget.js";
import server from "../../services/server.js";
import utils from "../../services/utils.js";
const TPL = `
<div class="note-info-widget">
@ -105,12 +106,12 @@ export default class NoteInfoWidget extends NoteContextAwareWidget {
this.$subTreeSize.empty().append($('<span class="bx bx-loader bx-spin"></span>'));
const noteSizeResp = await server.get(`stats/note-size/${this.noteId}`);
this.$noteSize.text(this.formatSize(noteSizeResp.noteSize));
this.$noteSize.text(utils.formatNoteSize(noteSizeResp.noteSize));
const subTreeResp = await server.get(`stats/subtree-size/${this.noteId}`);
if (subTreeResp.subTreeNoteCount > 1) {
this.$subTreeSize.text(`(subtree size: ${this.formatSize(subTreeResp.subTreeSize)} in ${subTreeResp.subTreeNoteCount} notes)`);
this.$subTreeSize.text(`(subtree size: ${utils.formatNoteSize(subTreeResp.subTreeSize)} in ${subTreeResp.subTreeNoteCount} notes)`);
}
else {
this.$subTreeSize.text("");
@ -142,18 +143,7 @@ export default class NoteInfoWidget extends NoteContextAwareWidget {
this.$calculateButton.show();
this.$noteSizesWrapper.hide();
}
formatSize(size) {
size = Math.max(Math.round(size / 1024), 1);
if (size < 1024) {
return `${size} KiB`;
}
else {
return `${Math.round(size / 102.4) / 10} MiB`;
}
}
entitiesReloadedEvent({loadResults}) {
if (loadResults.isNoteReloaded(this.noteId) || loadResults.isNoteContentReloaded(this.noteId)) {
this.refresh();

View file

@ -609,6 +609,17 @@ export default class TabRowWidget extends BasicWidget {
this.updateTabById(noteContext.mainNtxId || noteContext.ntxId);
}
noteContextReorderEvent({oldMainNtxId, newMainNtxId}) {
if (!oldMainNtxId || !newMainNtxId) {
// no need to update tab row
return;
}
// update tab id for the new main context
this.getTabById(oldMainNtxId).attr("data-ntx-id", newMainNtxId);
this.updateTabById(newMainNtxId);
}
updateTabById(ntxId) {
const $tab = this.getTabById(ntxId);