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], 'value': [v.notNull, v.isString],
'position': [v.notNull, v.isInteger] '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) => { eu.route(router, 'patch' ,'/etapi/attributes/:attributeId', (req, res, next) => {
const attribute = eu.getAndCheckAttribute(req.params.attributeId); 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.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(); attribute.save();

View File

@ -374,7 +374,7 @@ paths:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
patch: 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 operationId: patchBranchById
requestBody: requestBody:
required: true required: true
@ -456,7 +456,7 @@ paths:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
patch: 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 operationId: patchAttributeById
requestBody: requestBody:
required: true required: true

View File

@ -451,16 +451,23 @@ export default class TabManager extends Component {
this.tabsUpdate.scheduleUpdate(); this.tabsUpdate.scheduleUpdate();
} }
noteContextReorderEvent({ntxIdsInOrder}) { noteContextReorderEvent({ntxIdsInOrder, oldMainNtxId, newMainNtxId}) {
const order = {}; const order = Object.fromEntries(ntxIdsInOrder.map((v, i) => [v, i]));
let i = 0;
for (const ntxId of ntxIdsInOrder) {
order[ntxId] = i++;
}
this.children.sort((a, b) => order[a.ntxId] < order[b.ntxId] ? -1 : 1); 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(); 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 ApiLogWidget from "../widgets/api_log.js";
import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js"; import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js";
import ScriptExecutorWidget from "../widgets/ribbon_widgets/script_executor.js"; import ScriptExecutorWidget from "../widgets/ribbon_widgets/script_executor.js";
import MovePaneButton from "../widgets/buttons/move_pane_button.js";
export default class DesktopLayout { export default class DesktopLayout {
constructor(customWidgets) { constructor(customWidgets) {
@ -124,6 +125,8 @@ export default class DesktopLayout {
.child(new NoteIconWidget()) .child(new NoteIconWidget())
.child(new NoteTitleWidget()) .child(new NoteTitleWidget())
.child(new SpacerWidget(0, 1)) .child(new SpacerWidget(0, 1))
.child(new MovePaneButton(true))
.child(new MovePaneButton(false))
.child(new ClosePaneButton()) .child(new ClosePaneButton())
.child(new CreatePaneButton()) .child(new CreatePaneButton())
) )

View File

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

View File

@ -354,6 +354,17 @@ function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); 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 { export default {
reloadFrontendApp, reloadFrontendApp,
parseDate, parseDate,
@ -396,5 +407,6 @@ export default {
filterAttributeName, filterAttributeName,
isValidAttributeName, isValidAttributeName,
sleep, sleep,
escapeRegExp escapeRegExp,
formatNoteSize
}; };

View File

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

View File

@ -136,7 +136,7 @@ export default class FilePropertiesWidget extends NoteContextAwareWidget {
const noteComplement = await this.noteContext.getNoteComplement(); 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 // open doesn't work for protected notes since it works through browser which isn't in protected session
this.$openButton.toggle(!note.isProtected); this.$openButton.toggle(!note.isProtected);

View File

@ -1,5 +1,6 @@
import NoteContextAwareWidget from "../note_context_aware_widget.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js";
import server from "../../services/server.js"; import server from "../../services/server.js";
import utils from "../../services/utils.js";
const TPL = ` const TPL = `
<div class="note-info-widget"> <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>')); this.$subTreeSize.empty().append($('<span class="bx bx-loader bx-spin"></span>'));
const noteSizeResp = await server.get(`stats/note-size/${this.noteId}`); 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}`); const subTreeResp = await server.get(`stats/subtree-size/${this.noteId}`);
if (subTreeResp.subTreeNoteCount > 1) { 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 { else {
this.$subTreeSize.text(""); this.$subTreeSize.text("");
@ -142,18 +143,7 @@ export default class NoteInfoWidget extends NoteContextAwareWidget {
this.$calculateButton.show(); this.$calculateButton.show();
this.$noteSizesWrapper.hide(); 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}) { entitiesReloadedEvent({loadResults}) {
if (loadResults.isNoteReloaded(this.noteId) || loadResults.isNoteContentReloaded(this.noteId)) { if (loadResults.isNoteReloaded(this.noteId) || loadResults.isNoteContentReloaded(this.noteId)) {
this.refresh(); this.refresh();

View File

@ -609,6 +609,17 @@ export default class TabRowWidget extends BasicWidget {
this.updateTabById(noteContext.mainNtxId || noteContext.ntxId); 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) { updateTabById(ntxId) {
const $tab = this.getTabById(ntxId); const $tab = this.getTabById(ntxId);