From ce7489049f9dc2110f99f2d63e18aeea1f6243db Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Tue, 23 May 2023 10:32:24 +0800
Subject: [PATCH 01/40] put window on top
---
src/public/app/widgets/title_bar_buttons.js | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/src/public/app/widgets/title_bar_buttons.js b/src/public/app/widgets/title_bar_buttons.js
index e125e555b..832ddfef1 100644
--- a/src/public/app/widgets/title_bar_buttons.js
+++ b/src/public/app/widgets/title_bar_buttons.js
@@ -33,6 +33,7 @@ const TPL = `
+
@@ -47,10 +48,24 @@ export default class TitleBarButtonsWidget extends BasicWidget {
this.$widget = $(TPL);
this.contentSized();
+ const $topBtn = this.$widget.find(".top-btn");
const $minimizeBtn = this.$widget.find(".minimize-btn");
const $maximizeBtn = this.$widget.find(".maximize-btn");
const $closeBtn = this.$widget.find(".close-btn");
+ $topBtn.on('click', () => {
+ $topBtn.trigger('blur');
+ const remote = utils.dynamicRequire('@electron/remote');
+ const focusedWindow = remote.BrowserWindow.getFocusedWindow();
+ const isAlwaysOnTop = focusedWindow.isAlwaysOnTop()
+ if (isAlwaysOnTop) {
+ focusedWindow.setAlwaysOnTop(false)
+ $topBtn.css("background-color", "");
+ } else {
+ focusedWindow.setAlwaysOnTop(true);
+ $topBtn.css("background-color", "var(--accented-background-color)");
+ }
+ });
$minimizeBtn.on('click', () => {
$minimizeBtn.trigger('blur');
const remote = utils.dynamicRequire('@electron/remote');
From 63be25f5bb12679b62577e0333ea52d4bd61404a Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Tue, 23 May 2023 11:14:23 +0800
Subject: [PATCH 02/40] put window on top
---
src/public/app/widgets/title_bar_buttons.js | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/public/app/widgets/title_bar_buttons.js b/src/public/app/widgets/title_bar_buttons.js
index 832ddfef1..beb7aa1e7 100644
--- a/src/public/app/widgets/title_bar_buttons.js
+++ b/src/public/app/widgets/title_bar_buttons.js
@@ -52,7 +52,14 @@ export default class TitleBarButtonsWidget extends BasicWidget {
const $minimizeBtn = this.$widget.find(".minimize-btn");
const $maximizeBtn = this.$widget.find(".maximize-btn");
const $closeBtn = this.$widget.find(".close-btn");
-
+
+ //When the window is restarted, the window will not be reset when it is set to the top, so get the window status and set the icon background
+ (function() {
+ const remote = utils.dynamicRequire('@electron/remote');
+ if (remote.BrowserWindow.getFocusedWindow().isAlwaysOnTop()) {
+ $topBtn.css("background-color", "var(--accented-background-color)");
+ }
+ }());
$topBtn.on('click', () => {
$topBtn.trigger('blur');
const remote = utils.dynamicRequire('@electron/remote');
@@ -66,6 +73,7 @@ export default class TitleBarButtonsWidget extends BasicWidget {
$topBtn.css("background-color", "var(--accented-background-color)");
}
});
+
$minimizeBtn.on('click', () => {
$minimizeBtn.trigger('blur');
const remote = utils.dynamicRequire('@electron/remote');
From 039959a48b46131de15a65685a2bff2a62b4ec2e Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Tue, 23 May 2023 15:44:26 +0800
Subject: [PATCH 03/40] Put this window top.
---
src/public/app/widgets/title_bar_buttons.js | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/public/app/widgets/title_bar_buttons.js b/src/public/app/widgets/title_bar_buttons.js
index beb7aa1e7..0ba59ffb7 100644
--- a/src/public/app/widgets/title_bar_buttons.js
+++ b/src/public/app/widgets/title_bar_buttons.js
@@ -33,7 +33,7 @@ const TPL = `
-
+
@@ -58,6 +58,7 @@ export default class TitleBarButtonsWidget extends BasicWidget {
const remote = utils.dynamicRequire('@electron/remote');
if (remote.BrowserWindow.getFocusedWindow().isAlwaysOnTop()) {
$topBtn.css("background-color", "var(--accented-background-color)");
+ $topBtn.attr("title","The window has been pinned. ");
}
}());
$topBtn.on('click', () => {
@@ -68,9 +69,11 @@ export default class TitleBarButtonsWidget extends BasicWidget {
if (isAlwaysOnTop) {
focusedWindow.setAlwaysOnTop(false)
$topBtn.css("background-color", "");
+ $topBtn.attr("title","Put this window top. ");
} else {
focusedWindow.setAlwaysOnTop(true);
$topBtn.css("background-color", "var(--accented-background-color)");
+ $topBtn.attr("title","The window has been pinned. ");
}
});
From dc4983a013015ca52439e65e785bc28e026ed3fa Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Tue, 23 May 2023 15:49:03 +0800
Subject: [PATCH 04/40] Bring this window to the top
---
src/public/app/widgets/title_bar_buttons.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/public/app/widgets/title_bar_buttons.js b/src/public/app/widgets/title_bar_buttons.js
index 0ba59ffb7..fe09669e3 100644
--- a/src/public/app/widgets/title_bar_buttons.js
+++ b/src/public/app/widgets/title_bar_buttons.js
@@ -33,7 +33,7 @@ const TPL = `
-
+
@@ -58,7 +58,7 @@ export default class TitleBarButtonsWidget extends BasicWidget {
const remote = utils.dynamicRequire('@electron/remote');
if (remote.BrowserWindow.getFocusedWindow().isAlwaysOnTop()) {
$topBtn.css("background-color", "var(--accented-background-color)");
- $topBtn.attr("title","The window has been pinned. ");
+ $topBtn.attr("title","The window is already on top. ");
}
}());
$topBtn.on('click', () => {
@@ -69,11 +69,11 @@ export default class TitleBarButtonsWidget extends BasicWidget {
if (isAlwaysOnTop) {
focusedWindow.setAlwaysOnTop(false)
$topBtn.css("background-color", "");
- $topBtn.attr("title","Put this window top. ");
+ $topBtn.attr("title","Bring this window to the top. ");
} else {
focusedWindow.setAlwaysOnTop(true);
$topBtn.css("background-color", "var(--accented-background-color)");
- $topBtn.attr("title","The window has been pinned. ");
+ $topBtn.attr("title","The window is already on top. ");
}
});
From 66c25e7f2966c7a47b6f9469b587c75f13b5e075 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Tue, 23 May 2023 15:55:35 +0800
Subject: [PATCH 05/40] Bring this window to the top
---
src/public/app/widgets/title_bar_buttons.js | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/public/app/widgets/title_bar_buttons.js b/src/public/app/widgets/title_bar_buttons.js
index fe09669e3..a2f35937b 100644
--- a/src/public/app/widgets/title_bar_buttons.js
+++ b/src/public/app/widgets/title_bar_buttons.js
@@ -58,7 +58,6 @@ export default class TitleBarButtonsWidget extends BasicWidget {
const remote = utils.dynamicRequire('@electron/remote');
if (remote.BrowserWindow.getFocusedWindow().isAlwaysOnTop()) {
$topBtn.css("background-color", "var(--accented-background-color)");
- $topBtn.attr("title","The window is already on top. ");
}
}());
$topBtn.on('click', () => {
@@ -69,11 +68,9 @@ export default class TitleBarButtonsWidget extends BasicWidget {
if (isAlwaysOnTop) {
focusedWindow.setAlwaysOnTop(false)
$topBtn.css("background-color", "");
- $topBtn.attr("title","Bring this window to the top. ");
} else {
focusedWindow.setAlwaysOnTop(true);
$topBtn.css("background-color", "var(--accented-background-color)");
- $topBtn.attr("title","The window is already on top. ");
}
});
From 79dca12274b31d3dc681742b5ed0ea792bedbfc2 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Tue, 23 May 2023 20:50:03 +0800
Subject: [PATCH 06/40] top
---
.vscode/settings.json | 2 +-
src/public/app/widgets/title_bar_buttons.js | 15 +++++++++------
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 0e4b77b5c..b1cee4342 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,6 @@
{
"[javascript]": {
- "editor.defaultFormatter": "dbaeumer.vscode-eslint"
+ "editor.defaultFormatter": "vscode.typescript-language-features"
},
"[json]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
diff --git a/src/public/app/widgets/title_bar_buttons.js b/src/public/app/widgets/title_bar_buttons.js
index a2f35937b..24188d9ce 100644
--- a/src/public/app/widgets/title_bar_buttons.js
+++ b/src/public/app/widgets/title_bar_buttons.js
@@ -30,6 +30,9 @@ const TPL = `
height: 40px;
width: 40px;
}
+ .title-bar-buttons .top-btn.active{
+ background-color:var(--accented-background-color);
+ }
@@ -52,12 +55,12 @@ export default class TitleBarButtonsWidget extends BasicWidget {
const $minimizeBtn = this.$widget.find(".minimize-btn");
const $maximizeBtn = this.$widget.find(".maximize-btn");
const $closeBtn = this.$widget.find(".close-btn");
-
+
//When the window is restarted, the window will not be reset when it is set to the top, so get the window status and set the icon background
- (function() {
+ (function () {
const remote = utils.dynamicRequire('@electron/remote');
if (remote.BrowserWindow.getFocusedWindow().isAlwaysOnTop()) {
- $topBtn.css("background-color", "var(--accented-background-color)");
+ $topBtn.addClass('active');
}
}());
$topBtn.on('click', () => {
@@ -67,13 +70,13 @@ export default class TitleBarButtonsWidget extends BasicWidget {
const isAlwaysOnTop = focusedWindow.isAlwaysOnTop()
if (isAlwaysOnTop) {
focusedWindow.setAlwaysOnTop(false)
- $topBtn.css("background-color", "");
+ $topBtn.removeClass('active');
} else {
focusedWindow.setAlwaysOnTop(true);
- $topBtn.css("background-color", "var(--accented-background-color)");
+ $topBtn.addClass('active');
}
});
-
+
$minimizeBtn.on('click', () => {
$minimizeBtn.trigger('blur');
const remote = utils.dynamicRequire('@electron/remote');
From 01b2887b36c44f2ca519c299dd1b72f6e9aadcd3 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Sat, 27 May 2023 14:18:26 +0800
Subject: [PATCH 07/40] Keep this window on top.
---
src/public/app/widgets/title_bar_buttons.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/public/app/widgets/title_bar_buttons.js b/src/public/app/widgets/title_bar_buttons.js
index 24188d9ce..6a4f73ad5 100644
--- a/src/public/app/widgets/title_bar_buttons.js
+++ b/src/public/app/widgets/title_bar_buttons.js
@@ -36,7 +36,7 @@ const TPL = `
-
+
From 1a2beb941e8f77b7bf59586289bfa10bf1327e14 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Sat, 27 May 2023 14:23:50 +0800
Subject: [PATCH 08/40] keep this window on top
---
.vscode/settings.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b1cee4342..0e4b77b5c 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,6 @@
{
"[javascript]": {
- "editor.defaultFormatter": "vscode.typescript-language-features"
+ "editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[json]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
From e1b67e20ec66b0d295fe567d21baeb68ad23c72a Mon Sep 17 00:00:00 2001
From: dymani
Date: Tue, 30 May 2023 02:24:56 +0800
Subject: [PATCH 09/40] Add buttons to move split panes
---
src/public/app/layouts/desktop_layout.js | 3 +
.../app/widgets/buttons/move_pane_button.js | 55 +++++++++++++++++++
.../containers/split_note_container.js | 38 +++++++++++++
3 files changed, 96 insertions(+)
create mode 100644 src/public/app/widgets/buttons/move_pane_button.js
diff --git a/src/public/app/layouts/desktop_layout.js b/src/public/app/layouts/desktop_layout.js
index 17dcc99a7..62e6b3ed1 100644
--- a/src/public/app/layouts/desktop_layout.js
+++ b/src/public/app/layouts/desktop_layout.js
@@ -75,6 +75,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) {
@@ -123,6 +124,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())
)
diff --git a/src/public/app/widgets/buttons/move_pane_button.js b/src/public/app/widgets/buttons/move_pane_button.js
new file mode 100644
index 000000000..9ea4dbe61
--- /dev/null
+++ b/src/public/app/widgets/buttons/move_pane_button.js
@@ -0,0 +1,55 @@
+import OnClickButtonWidget from "./onclick_button.js";
+import appContext from "../../components/app_context.js";
+
+export default class MovePaneButton extends OnClickButtonWidget {
+ 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;
+ }
+ }
+
+ initialRenderCompleteEvent() {
+ this.refresh();
+ super.initialRenderCompleteEvent();
+ }
+
+ async noteContextRemovedEvent({ntxIds}) {
+ this.refresh();
+ }
+
+ async newNoteContextCreatedEvent({noteContext}) {
+ this.refresh();
+ }
+
+ async noteContextSwitchEvent() {
+ this.refresh();
+ }
+
+ async noteContextReorderEvent({ntxIdsInOrder}) {
+ this.refresh();
+ }
+
+ 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");
+ }
+}
diff --git a/src/public/app/widgets/containers/split_note_container.js b/src/public/app/widgets/containers/split_note_container.js
index 66356164d..854ccfd23 100644
--- a/src/public/app/widgets/containers/split_note_container.js
+++ b/src/public/app/widgets/containers/split_note_container.js
@@ -1,5 +1,6 @@
import FlexContainer from "./flex_container.js";
import appContext from "../../components/app_context.js";
+import NoteContext from "../../components/note_context.js";
export default class SplitNoteContainer extends FlexContainer {
constructor(widgetFactory) {
@@ -74,6 +75,43 @@ 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 otherIndex = currentIndex + (isMovingLeft ? -1 : 1);
+
+ if (currentIndex === -1 || otherIndex < 0 || otherIndex >= contexts.length) {
+ logError("invalid context!");
+ return;
+ }
+
+ if (contexts[currentIndex].isEmpty() && contexts[otherIndex].isEmpty())
+ // no op
+ return;
+
+ const currentId = contexts[currentIndex].ntxId;
+ const currentPath = contexts[currentIndex].notePath;
+
+ const otherId = contexts[otherIndex].ntxId;
+ const otherPath = contexts[otherIndex].notePath;
+
+ if (!!currentPath)
+ await appContext.tabManager.switchToNoteContext(otherId, currentPath);
+ if (!!otherPath)
+ await appContext.tabManager.switchToNoteContext(currentId, otherPath);
+
+ // activate context that now contains the original note
+ await appContext.tabManager.activateNoteContext(otherId);
+
+ this.triggerEvent('noteContextSwitch');
+ }
+
activeContextChangedEvent() {
this.refresh();
}
From 2a399069936bb3e0c094ca52ebbad493ed529122 Mon Sep 17 00:00:00 2001
From: dymani
Date: Tue, 30 May 2023 04:22:51 +0800
Subject: [PATCH 10/40] Cleanup
---
src/public/app/widgets/containers/split_note_container.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/public/app/widgets/containers/split_note_container.js b/src/public/app/widgets/containers/split_note_container.js
index 854ccfd23..3a17b7d20 100644
--- a/src/public/app/widgets/containers/split_note_container.js
+++ b/src/public/app/widgets/containers/split_note_container.js
@@ -1,6 +1,5 @@
import FlexContainer from "./flex_container.js";
import appContext from "../../components/app_context.js";
-import NoteContext from "../../components/note_context.js";
export default class SplitNoteContainer extends FlexContainer {
constructor(widgetFactory) {
From 735852b3c1dc9d642bfc01f603b1f84aec9e9e1b Mon Sep 17 00:00:00 2001
From: dymani
Date: Wed, 31 May 2023 01:53:55 +0800
Subject: [PATCH 11/40] Use noteContextReorder event instead
---
src/public/app/components/tab_manager.js | 13 ++++---
.../app/widgets/buttons/close_pane_button.js | 4 +++
.../containers/split_note_container.js | 36 ++++++++++---------
src/public/app/widgets/tab_row.js | 16 +++++++++
4 files changed, 46 insertions(+), 23 deletions(-)
diff --git a/src/public/app/components/tab_manager.js b/src/public/app/components/tab_manager.js
index e5ca32c45..ace30105f 100644
--- a/src/public/app/components/tab_manager.js
+++ b/src/public/app/components/tab_manager.js
@@ -451,16 +451,15 @@ 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, mainNtxIdsInOrder}) {
+ const order = Object.fromEntries(ntxIdsInOrder.map((v, i) => [v, i]));
this.children.sort((a, b) => order[a.ntxId] < order[b.ntxId] ? -1 : 1);
+ if (!!mainNtxIdsInOrder && mainNtxIdsInOrder.length === this.children.length) {
+ this.children.forEach((c, i) => c.mainNtxId = mainNtxIdsInOrder[i]);
+ }
+
this.tabsUpdate.scheduleUpdate();
}
diff --git a/src/public/app/widgets/buttons/close_pane_button.js b/src/public/app/widgets/buttons/close_pane_button.js
index 690dcac6f..220fd2cca 100644
--- a/src/public/app/widgets/buttons/close_pane_button.js
+++ b/src/public/app/widgets/buttons/close_pane_button.js
@@ -7,6 +7,10 @@ export default class ClosePaneButton extends OnClickButtonWidget {
&& this.noteContext && !!this.noteContext.mainNtxId;
}
+ async noteContextReorderEvent({ntxIdsInOrder}) {
+ this.refresh();
+ }
+
constructor() {
super();
diff --git a/src/public/app/widgets/containers/split_note_container.js b/src/public/app/widgets/containers/split_note_container.js
index 3a17b7d20..988c537b1 100644
--- a/src/public/app/widgets/containers/split_note_container.js
+++ b/src/public/app/widgets/containers/split_note_container.js
@@ -83,32 +83,36 @@ export default class SplitNoteContainer extends FlexContainer {
const contexts = appContext.tabManager.noteContexts;
const currentIndex = contexts.findIndex(c => c.ntxId === ntxId);
- const otherIndex = currentIndex + (isMovingLeft ? -1 : 1);
+ const leftIndex = isMovingLeft ? currentIndex - 1 : currentIndex;
- if (currentIndex === -1 || otherIndex < 0 || otherIndex >= contexts.length) {
+ if (currentIndex === -1 || leftIndex < 0 || leftIndex + 1 >= contexts.length) {
logError("invalid context!");
return;
}
- if (contexts[currentIndex].isEmpty() && contexts[otherIndex].isEmpty())
+ if (contexts[leftIndex].isEmpty() && contexts[leftIndex + 1].isEmpty())
// no op
return;
- const currentId = contexts[currentIndex].ntxId;
- const currentPath = contexts[currentIndex].notePath;
-
- const otherId = contexts[otherIndex].ntxId;
- const otherPath = contexts[otherIndex].notePath;
-
- if (!!currentPath)
- await appContext.tabManager.switchToNoteContext(otherId, currentPath);
- if (!!otherPath)
- await appContext.tabManager.switchToNoteContext(currentId, otherPath);
+ const ntxIds = contexts.map(c => c.ntxId);
+ const mainNtxIds = contexts.map(c => c.mainNtxId);
+
+ this.triggerCommand("noteContextReorder", {
+ ntxIdsInOrder: [
+ ...ntxIds.slice(0, leftIndex),
+ ntxIds[leftIndex + 1],
+ ntxIds[leftIndex],
+ ...ntxIds.slice(leftIndex + 2),
+ ],
+ oldNtxIdsInOrder: ntxIds,
+ mainNtxIdsInOrder: mainNtxIds.map(id => id === ntxIds[leftIndex] ? ntxIds[leftIndex + 1] : id)
+ });
+
+ 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(otherId);
-
- this.triggerEvent('noteContextSwitch');
+ await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]);
}
activeContextChangedEvent() {
diff --git a/src/public/app/widgets/tab_row.js b/src/public/app/widgets/tab_row.js
index 55a5da24c..6e15f3458 100644
--- a/src/public/app/widgets/tab_row.js
+++ b/src/public/app/widgets/tab_row.js
@@ -609,6 +609,22 @@ export default class TabRowWidget extends BasicWidget {
this.updateTabById(noteContext.mainNtxId || noteContext.ntxId);
}
+ noteContextReorderEvent({ntxIdsInOrder, oldNtxIdsInOrder, mainNtxIdsInOrder}) {
+ if (!oldNtxIdsInOrder || !mainNtxIdsInOrder
+ || ntxIdsInOrder.length !== oldNtxIdsInOrder.length
+ || ntxIdsInOrder.length !== mainNtxIdsInOrder.length
+ )
+ return;
+
+ ntxIdsInOrder.forEach((id, i) => {
+ // update main context that is not a tab
+ if (!mainNtxIdsInOrder[i] && this.getTabById(id).length === 0) {
+ this.getTabById(oldNtxIdsInOrder[i]).attr("data-ntx-id", id);
+ this.updateTabById(id);
+ }
+ });
+ }
+
updateTabById(ntxId) {
const $tab = this.getTabById(ntxId);
From 64aed9b4620df36277556bb6d7c8778bc3dd1a97 Mon Sep 17 00:00:00 2001
From: zadam
Date: Tue, 30 May 2023 22:52:48 +0200
Subject: [PATCH 12/40] display icon for non-supported note types in content
renderer
---
package-lock.json | 4 ++--
src/public/app/services/note_content_renderer.js | 10 ++++++----
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 74aabd229..6055a636e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "trilium",
- "version": "0.60.0-beta",
+ "version": "0.60.1-beta",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "trilium",
- "version": "0.60.0-beta",
+ "version": "0.60.1-beta",
"hasInstallScript": true,
"license": "AGPL-3.0-only",
"dependencies": {
diff --git a/src/public/app/services/note_content_renderer.js b/src/public/app/services/note_content_renderer.js
index 2fb805799..6bb47d9f3 100644
--- a/src/public/app/services/note_content_renderer.js
+++ b/src/public/app/services/note_content_renderer.js
@@ -157,9 +157,6 @@ async function getRenderedContent(note, options = {}) {
$renderedContent.append($("
").text("Error parsing content. Please check console.error() for more details."));
}
}
- else if (type === 'book') {
- // nothing, book doesn't have its own content
- }
else if (!options.tooltip && type === 'protectedSession') {
const $button = $(``)
.on('click', protectedSessionService.enterProtectedSession);
@@ -172,7 +169,12 @@ async function getRenderedContent(note, options = {}) {
);
}
else {
- $renderedContent.append($("
Content of this note cannot be displayed in the book format
"));
+ $renderedContent.append(
+ $("
")
+ .css("text-align", "center")
+ .css("font-size", "500%")
+ .append($("").addClass(note.getIcon()))
+ );
}
$renderedContent.addClass(note.getCssClass());
From 375dba3264751fe30cb33fd82f210c3d21c11f63 Mon Sep 17 00:00:00 2001
From: zadam
Date: Tue, 30 May 2023 23:54:11 +0200
Subject: [PATCH 13/40] fixed infinite recursion with search notes
---
src/public/app/entities/fnote.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/public/app/entities/fnote.js b/src/public/app/entities/fnote.js
index 6af816a44..eff96df81 100644
--- a/src/public/app/entities/fnote.js
+++ b/src/public/app/entities/fnote.js
@@ -392,7 +392,7 @@ class FNote {
for (const parentNote of this.getParentNotes()) {
if (parentNote.noteId === 'root') {
return false;
- } else if (parentNote.noteId === '_hidden') {
+ } else if (parentNote.noteId === '_hidden' || parentNote.type === 'search') {
continue;
}
From d26a0fae175d1abf6d24fdab5ca03d4c257f57ae Mon Sep 17 00:00:00 2001
From: zadam
Date: Wed, 31 May 2023 00:03:20 +0200
Subject: [PATCH 14/40] if a note context has sub contexts, then it has to be
saved even if empty, fixes #3985
---
src/public/app/components/note_context.js | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/public/app/components/note_context.js b/src/public/app/components/note_context.js
index 3df6b206e..c22a87ac6 100644
--- a/src/public/app/components/note_context.js
+++ b/src/public/app/components/note_context.js
@@ -176,9 +176,12 @@ class NoteContext extends Component {
}
getTabState() {
- if (!this.notePath && this.hoistedNoteId === 'root') {
+ if (this.hoistedNoteId !== 'root') {
// keeping empty hoisted tab is esp. important for mobile (e.g. opened launcher config)
- return null;
+
+ if (!this.notePath && this.getSubContexts().length === 0) {
+ return null;
+ }
}
return {
From 86861f6ec36b6015c20dd0b4529b627fc8f92667 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Wed, 31 May 2023 18:32:33 +0800
Subject: [PATCH 15/40] Show highlighted text in the left pane
---
src/public/app/layouts/desktop_layout.js | 2 +
src/public/app/widgets/highlighted_text.js | 267 ++++++++++++++++++
.../widgets/type_widgets/content_widget.js | 2 +
.../options/text_notes/highlighted_text.js | 90 ++++++
src/routes/api/options.js | 2 +
src/services/options_init.js | 2 +
6 files changed, 365 insertions(+)
create mode 100644 src/public/app/widgets/highlighted_text.js
create mode 100644 src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
diff --git a/src/public/app/layouts/desktop_layout.js b/src/public/app/layouts/desktop_layout.js
index 17dcc99a7..3a9ec2930 100644
--- a/src/public/app/layouts/desktop_layout.js
+++ b/src/public/app/layouts/desktop_layout.js
@@ -44,6 +44,7 @@ import BacklinksWidget from "../widgets/floating_buttons/zpetne_odkazy.js";
import SharedInfoWidget from "../widgets/shared_info.js";
import FindWidget from "../widgets/find.js";
import TocWidget from "../widgets/toc.js";
+import HltWidget from "../widgets/highlighted_text.js";
import BulkActionsDialog from "../widgets/dialogs/bulk_actions.js";
import AboutDialog from "../widgets/dialogs/about.js";
import HelpDialog from "../widgets/dialogs/help.js";
@@ -181,6 +182,7 @@ export default class DesktopLayout {
)
.child(new RightPaneContainer()
.child(new TocWidget())
+ .child(new HltWidget())
.child(...this.customWidgets.get('right-pane'))
)
)
diff --git a/src/public/app/widgets/highlighted_text.js b/src/public/app/widgets/highlighted_text.js
new file mode 100644
index 000000000..3dbfc9b5c
--- /dev/null
+++ b/src/public/app/widgets/highlighted_text.js
@@ -0,0 +1,267 @@
+/**
+ * Widget: Show highlighted text in the right pane
+ *
+ * By design there's no support for nonsensical or malformed constructs:
+ * - For example, if there is a formula in the middle of the highlighted text, the two ends of the formula will be regarded as two entries
+ */
+
+import attributeService from "../services/attributes.js";
+import RightPanelWidget from "./right_panel_widget.js";
+import options from "../services/options.js";
+import OnClickButtonWidget from "./buttons/onclick_button.js";
+
+const TPL = `
+
+
+
+
`;
+
+export default class HltWidget extends RightPanelWidget {
+ constructor() {
+ super();
+
+ this.closeHltButton = new CloseHltButton();
+ this.child(this.closeHltButton);
+ }
+
+ get widgetTitle() {
+ return "Highlighted Text";
+ }
+
+ isEnabled() {
+ return super.isEnabled()
+ && this.note.type === 'text'
+ && !this.noteContext.viewScope.hltTemporarilyHidden
+ && this.noteContext.viewScope.viewMode === 'default';
+ }
+
+ async doRenderBody() {
+ this.$body.empty().append($(TPL));
+ this.$hlt = this.$body.find('.hlt');
+ this.$body.find('.hlt-widget').append(this.closeHltButton.render());
+ }
+
+ async refreshWithNote(note) {
+ const hltLabel = note.getLabel('hlt');
+
+ if (hltLabel?.value === 'hide') {
+ this.toggleInt(false);
+ this.triggerCommand("reEvaluateRightPaneVisibility");
+ return;
+ }
+
+ let $hlt = "", hltColors = [], hltBgColors = [];
+
+ let optionsHltColors = JSON.parse(options.get('highlightedTextColors'));
+ let optionsHltBgColors = JSON.parse(options.get('highlightedTextBgColors'));
+ // Check for type text unconditionally in case alwaysShowWidget is set
+ if (this.note.type === 'text') {
+ const { content } = await note.getNoteComplement();
+ //hltColors/hltBgColors are the colors/background-color that appear in notes and in options
+ ({ $hlt, hltColors, hltBgColors } = await this.getHlt(content, optionsHltColors, optionsHltBgColors));
+ }
+ this.$hlt.html($hlt);
+ this.toggleInt(
+ ["", "show"].includes(hltLabel?.value)
+ || hltColors!=""
+ || hltBgColors!=""
+ );
+
+ this.triggerCommand("reEvaluateRightPaneVisibility");
+ }
+ //Converts color values in RGB, RGBA, or HSL format to hexadecimal format, removing transparency
+ colorToHex(color) {
+ function rgbToHex(rgb) {
+ // Converts color values in RGB or RGBA format to hexadecimal format
+ var rgba = rgb.match(/\d+/g);
+ var r = parseInt(rgba[0]);
+ var g = parseInt(rgba[1]);
+ var b = parseInt(rgba[2]);
+ var hex = "#";
+ hex += (r < 16 ? "0" : "") + r.toString(16);
+ hex += (g < 16 ? "0" : "") + g.toString(16);
+ hex += (b < 16 ? "0" : "") + b.toString(16);
+ return hex;
+ }
+
+ function hslToHex(hsl) {
+ // Convert color values in HSL format to RGB format and then to hexadecimal format
+ var hslValues = hsl.match(/\d+(\.\d+)?/g);
+ var h = parseFloat(hslValues[0]) / 360;
+ var s = parseFloat(hslValues[1]) / 100;
+ var l = parseFloat(hslValues[2]) / 100;
+ var r, g, b;
+
+ if (s === 0) {
+ r = g = b = l; // achromatic
+ } else {
+ function hueToRgb(p, q, t) {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+ return p;
+ }
+
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hueToRgb(p, q, h + 1 / 3);
+ g = hueToRgb(p, q, h);
+ b = hueToRgb(p, q, h - 1 / 3);
+ }
+
+ var hex = "#";
+ hex += (Math.round(r * 255) < 16 ? "0" : "") + Math.round(r * 255).toString(16);
+ hex += (Math.round(g * 255) < 16 ? "0" : "") + Math.round(g * 255).toString(16);
+ hex += (Math.round(b * 255) < 16 ? "0" : "") + Math.round(b * 255).toString(16);
+ return hex;
+ }
+ if (color.indexOf("rgb") !== -1) {
+ return rgbToHex(color);
+ } else if (color.indexOf("hsl") !== -1) {
+ return hslToHex(color);
+ } else {
+ return "";
+ }
+ }
+ // Determine whether the highlighted color is in the options, avoid errors caused by errors in color conversion,
+ // and the error of each value is acceptable within 2
+ hexIsInOptionHexs(targetColor, optionColors){
+ for (let i = 0; i < optionColors.length; i++) {
+ if (Math.abs(parseInt(optionColors[i].slice(1, 3), 16) - parseInt(targetColor.slice(1, 3), 16)) > 2) { continue; }
+ if (Math.abs(parseInt(optionColors[i].slice(3, 5), 16) - parseInt(targetColor.slice(3, 5), 16)) > 2) { continue; }
+ if (Math.abs(parseInt(optionColors[i].slice(5, 7), 16) - parseInt(targetColor.slice(5, 7), 16)) > 2) { continue; }
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Builds a jquery table of helight text.
+ */
+ getHlt(html, optionsHltColors, optionsHltBgColors) {
+ const hltBCs = $(html).find(`span[style*="background-color"],span[style*="color"]`)
+ const $hlt = $("");
+ let hltColors = [];
+ let hltBgColors = [];
+ for (let hltIndex = 0; hltIndex');
+
+ if (color != "") {
+ var hexColor = this.colorToHex(color);
+ if (this.hexIsInOptionHexs(hexColor,optionsHltColors)) {
+ $li.html(hltText)
+ hltColors.push(hexColor);
+ liDisplay=true;
+ }
+ }
+ if (bgColor != "") {
+ var hexBgColor = this.colorToHex(bgColor);
+ if (this.hexIsInOptionHexs(hexBgColor,optionsHltBgColors)) {
+ //When you need to add a background color, in order to make the display more comfortable, change the background color to transparent
+ $li.html(hltText.css("background-color", hexBgColor+"80"))
+ hltBgColors.push(hexBgColor);
+ liDisplay=true;
+ }
+ }
+ if(!liDisplay){
+ $li.css("display","none");
+ }
+ //The font color and background color may be nested or adjacent to each other. At this time, connect the front and back li to avoid interruption
+ if(hltIndex!=0 && hltBCs[hltIndex-1].nextSibling ===hltBCs[hltIndex] && $hlt.children().last().css("display")!="none"){
+ $hlt.children().last().append($li.html());
+ }else{
+ $li.on("click", () => this.jumpToHlt(hltIndex));
+ $hlt.append($li);
+ }
+
+ };
+ return {
+ $hlt,
+ hltColors,
+ hltBgColors
+ };
+ }
+
+ async jumpToHlt(hltIndex) {
+ const isReadOnly = await this.noteContext.isReadOnly();
+ if (isReadOnly) {
+ const $container = await this.noteContext.getContentElement();
+ const hltElement = $container.find(`span[style*="background-color"],span[style*="color"]`)[hltIndex];
+
+ if (hltElement != null) {
+ hltElement.scrollIntoView({ behavior: "smooth", block: "center" });
+ }
+ } else {
+ const textEditor = await this.noteContext.getTextEditor();
+ $(textEditor.editing.view.domRoots.values().next().value).find(`span[style*="background-color"],span[style*="color"]`)[hltIndex].scrollIntoView({
+ behavior: "smooth", block: "center"
+ });
+ }
+ }
+
+ async closeHltCommand() {
+ this.noteContext.viewScope.hltTemporarilyHidden = true;
+ await this.refresh();
+ this.triggerCommand('reEvaluateRightPaneVisibility');
+ }
+
+ async entitiesReloadedEvent({ loadResults }) {
+ if (loadResults.isNoteContentReloaded(this.noteId)) {
+ await this.refresh();
+ } else if (loadResults.getAttributes().find(attr => attr.type === 'label'
+ && (attr.name.toLowerCase().includes('readonly') || attr.name === 'hlt')
+ && attributeService.isAffecting(attr, this.note))) {
+ await this.refresh();
+ }
+ }
+}
+
+
+class CloseHltButton extends OnClickButtonWidget {
+ constructor() {
+ super();
+
+ this.icon("bx-x")
+ .title("Close HLT")
+ .titlePlacement("bottom")
+ .onClick((widget, e) => {
+ e.stopPropagation();
+
+ widget.triggerCommand("closeHlt");
+ })
+ .class("icon-action close-hlt");
+ }
+}
diff --git a/src/public/app/widgets/type_widgets/content_widget.js b/src/public/app/widgets/type_widgets/content_widget.js
index 967c996e5..f7a4846bd 100644
--- a/src/public/app/widgets/type_widgets/content_widget.js
+++ b/src/public/app/widgets/type_widgets/content_widget.js
@@ -7,6 +7,7 @@ import MaxContentWidthOptions from "./options/appearance/max_content_width.js";
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 HighlightedTextOptions from "./options/text_notes/highlighted_text.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";
@@ -61,6 +62,7 @@ const CONTENT_WIDGETS = {
_optionsTextNotes: [
HeadingStyleOptions,
TableOfContentsOptions,
+ HighlightedTextOptions,
TextAutoReadOnlySizeOptions
],
_optionsCodeNotes: [
diff --git a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
new file mode 100644
index 000000000..7fc2ce825
--- /dev/null
+++ b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
@@ -0,0 +1,90 @@
+import OptionsWidget from "../options_widget.js";
+
+const TPL = `
+
+
+
Highlighted Text
+
+ Displays highlighted text in the left pane. You can customize the highlighted text displayed in the left pane:
+ Text color:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Background color:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -12671,7 +13359,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BNoteRevision.html b/docs/backend_api/BNoteRevision.html
index 0951fa14d..3a9ccf0d3 100644
--- a/docs/backend_api/BNoteRevision.html
+++ b/docs/backend_api/BNoteRevision.html
@@ -2174,7 +2174,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BOption.html b/docs/backend_api/BOption.html
index b5990cb92..849051190 100644
--- a/docs/backend_api/BOption.html
+++ b/docs/backend_api/BOption.html
@@ -267,7 +267,7 @@
@@ -1319,7 +1319,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BRecentNote.html b/docs/backend_api/BRecentNote.html
index b36b38298..cdda77c51 100644
--- a/docs/backend_api/BRecentNote.html
+++ b/docs/backend_api/BRecentNote.html
@@ -1251,7 +1251,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BackendScriptApi.html b/docs/backend_api/BackendScriptApi.html
index 12800f161..ba6ffbc2b 100644
--- a/docs/backend_api/BackendScriptApi.html
+++ b/docs/backend_api/BackendScriptApi.html
@@ -3254,7 +3254,7 @@ JSON MIME type. See also createNewNote() for more options.
-
@@ -3262,7 +3262,7 @@ JSON MIME type. See also createNewNote() for more options.
- If there's no branch between note and parent note, create one. Otherwise, do nothing.
+ If there's no branch between note and parent note, create one. Otherwise, do nothing. Returns the new or existing branch.
@@ -3437,7 +3437,7 @@ JSON MIME type. See also createNewNote() for more options.
-void
+Object
@@ -7889,7 +7889,7 @@ exists, then we'll use that transaction.
diff --git a/docs/backend_api/becca_entities_abstract_becca_entity.js.html b/docs/backend_api/becca_entities_abstract_becca_entity.js.html
index 4036de3f6..b64387b2d 100644
--- a/docs/backend_api/becca_entities_abstract_becca_entity.js.html
+++ b/docs/backend_api/becca_entities_abstract_becca_entity.js.html
@@ -212,7 +212,7 @@ module.exports = AbstractBeccaEntity;
diff --git a/docs/backend_api/becca_entities_battribute.js.html b/docs/backend_api/becca_entities_battribute.js.html
index 83fff96e5..d9715c60b 100644
--- a/docs/backend_api/becca_entities_battribute.js.html
+++ b/docs/backend_api/becca_entities_battribute.js.html
@@ -124,7 +124,7 @@ class BAttribute extends AbstractBeccaEntity {
}
if (this.type === 'relation' && !(this.value in this.becca.notes)) {
- throw new Error(`Cannot save relation '${this.name}' of note '${this.noteId}' since it target not existing note '${this.value}'.`);
+ throw new Error(`Cannot save relation '${this.name}' of note '${this.noteId}' since it targets not existing note '${this.value}'.`);
}
}
@@ -276,7 +276,7 @@ module.exports = BAttribute;
diff --git a/docs/backend_api/becca_entities_bbranch.js.html b/docs/backend_api/becca_entities_bbranch.js.html
index b4fa7f567..b2261711d 100644
--- a/docs/backend_api/becca_entities_bbranch.js.html
+++ b/docs/backend_api/becca_entities_bbranch.js.html
@@ -319,7 +319,7 @@ module.exports = BBranch;
diff --git a/docs/backend_api/becca_entities_betapi_token.js.html b/docs/backend_api/becca_entities_betapi_token.js.html
index 92ab19328..329843522 100644
--- a/docs/backend_api/becca_entities_betapi_token.js.html
+++ b/docs/backend_api/becca_entities_betapi_token.js.html
@@ -120,7 +120,7 @@ module.exports = BEtapiToken;
diff --git a/docs/backend_api/becca_entities_bnote.js.html b/docs/backend_api/becca_entities_bnote.js.html
index 97889071a..0c729d838 100644
--- a/docs/backend_api/becca_entities_bnote.js.html
+++ b/docs/backend_api/becca_entities_bnote.js.html
@@ -125,7 +125,7 @@ class BNote extends AbstractBeccaEntity {
* @private */
this.parents = [];
/** @type {BNote[]}
- * @private*/
+ * @private */
this.children = [];
/** @type {BAttribute[]}
* @private */
@@ -135,11 +135,11 @@ class BNote extends AbstractBeccaEntity {
* @private */
this.__attributeCache = null;
/** @type {BAttribute[]|null}
- * @private*/
+ * @private */
this.inheritableAttributeCache = null;
/** @type {BAttribute[]}
- * @private*/
+ * @private */
this.targetRelations = [];
this.becca.addNote(this.noteId, this);
@@ -560,6 +560,20 @@ class BNote extends AbstractBeccaEntity {
*/
hasLabel(name, value) { return this.hasAttribute(LABEL, name, value); }
+ /**
+ * @param {string} name - label name
+ * @returns {boolean} true if label exists (including inherited) and does not have "false" value.
+ */
+ isLabelTruthy(name) {
+ const label = this.getLabel(name);
+
+ if (!label) {
+ return false;
+ }
+
+ return label && label.value !== 'false';
+ }
+
/**
* @param {string} name - label name
* @param {string} [value] - label value
@@ -761,6 +775,21 @@ class BNote extends AbstractBeccaEntity {
return this.hasAttribute('label', 'archived');
}
+ areAllNotePathsArchived() {
+ // there's a slight difference between note being itself archived and all its note paths being archived
+ // - note is archived when it itself has an archived label or inherits it
+ // - note does not have or inherit archived label, but each note paths contains a note with (non-inheritable)
+ // archived label
+
+ const bestNotePathRecord = this.getSortedNotePathRecords()[0];
+
+ if (!bestNotePathRecord) {
+ throw new Error(`No note path available for note '${this.noteId}'`);
+ }
+
+ return bestNotePathRecord.isArchived;
+ }
+
hasInheritableArchivedLabel() {
for (const attr of this.getAttributes()) {
if (attr.name === 'archived' && attr.type === LABEL && attr.isInheritable) {
@@ -1164,6 +1193,8 @@ class BNote extends AbstractBeccaEntity {
}
/**
+ * Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles)
+ *
* @returns {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path)
*/
getAllNotePaths() {
@@ -1171,18 +1202,73 @@ class BNote extends AbstractBeccaEntity {
return [['root']];
}
- const notePaths = [];
+ const parentNotes = this.getParentNotes();
+ let notePaths = [];
- for (const parentNote of this.getParentNotes()) {
- for (const parentPath of parentNote.getAllNotePaths()) {
- parentPath.push(this.noteId);
- notePaths.push(parentPath);
- }
+ if (parentNotes.length === 1) { // optimization for most common case
+ notePaths = parentNotes[0].getAllNotePaths();
+ } else {
+ notePaths = parentNotes.flatMap(parentNote => parentNote.getAllNotePaths());
+ }
+
+ for (const notePath of notePaths) {
+ notePath.push(this.noteId);
}
return notePaths;
}
+ /**
+ * @param {string} [hoistedNoteId='root']
+ * @return {Array<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array<string>, isHidden: boolean}>}
+ */
+ getSortedNotePathRecords(hoistedNoteId = 'root') {
+ const isHoistedRoot = hoistedNoteId === 'root';
+
+ const notePaths = this.getAllNotePaths().map(path => ({
+ notePath: path,
+ isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
+ isArchived: path.some(noteId => this.becca.notes[noteId].isArchived),
+ isHidden: path.includes('_hidden')
+ }));
+
+ notePaths.sort((a, b) => {
+ if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
+ return a.isInHoistedSubTree ? -1 : 1;
+ } else if (a.isArchived !== b.isArchived) {
+ return a.isArchived ? 1 : -1;
+ } else if (a.isHidden !== b.isHidden) {
+ return a.isHidden ? 1 : -1;
+ } else {
+ return a.notePath.length - b.notePath.length;
+ }
+ });
+
+ return notePaths;
+ }
+
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string[]} array of noteIds constituting the particular note path
+ */
+ getBestNotePath(hoistedNoteId = 'root') {
+ return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath;
+ }
+
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string} serialized note path (e.g. 'root/a1h315/js725h')
+ */
+ getBestNotePathString(hoistedNoteId = 'root') {
+ const notePath = this.getBestNotePath(hoistedNoteId);
+
+ return notePath?.join("/");
+ }
+
/**
* @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree
*/
@@ -1196,9 +1282,7 @@ class BNote extends AbstractBeccaEntity {
return false;
} else if (parentNote.noteId === '_hidden') {
continue;
- }
-
- if (!parentNote.isHiddenCompletely()) {
+ } else if (!parentNote.isHiddenCompletely()) {
return false;
}
}
@@ -1392,7 +1476,7 @@ class BNote extends AbstractBeccaEntity {
/**
* @param parentNoteId
- * @returns {{success: boolean, message: string}}
+ * @returns {{success: boolean, message: string, branchId: string, notePath: string}}
*/
cloneTo(parentNoteId) {
const cloningService = require("../../services/cloning");
@@ -1550,7 +1634,7 @@ module.exports = BNote;
diff --git a/docs/backend_api/becca_entities_bnote_revision.js.html b/docs/backend_api/becca_entities_bnote_revision.js.html
index c140f1d10..ea663e51f 100644
--- a/docs/backend_api/becca_entities_bnote_revision.js.html
+++ b/docs/backend_api/becca_entities_bnote_revision.js.html
@@ -194,12 +194,14 @@ class BNoteRevision extends AbstractBeccaEntity {
utcDateLastEdited: this.utcDateLastEdited,
utcDateCreated: this.utcDateCreated,
utcDateModified: this.utcDateModified,
+ content: this.content, // used when retrieving full note revision to frontend
contentLength: this.contentLength
};
}
getPojoToSave() {
const pojo = this.getPojo();
+ delete pojo.content; // not getting persisted
delete pojo.contentLength; // not getting persisted
if (pojo.isProtected) {
@@ -233,7 +235,7 @@ module.exports = BNoteRevision;
diff --git a/docs/backend_api/becca_entities_boption.js.html b/docs/backend_api/becca_entities_boption.js.html
index ab2c22f51..0f2856fdc 100644
--- a/docs/backend_api/becca_entities_boption.js.html
+++ b/docs/backend_api/becca_entities_boption.js.html
@@ -44,6 +44,11 @@ class BOption extends AbstractBeccaEntity {
constructor(row) {
super();
+ this.updateFromRow(row);
+ this.becca.options[this.name] = this;
+ }
+
+ updateFromRow(row) {
/** @type {string} */
this.name = row.name;
/** @type {string} */
@@ -52,8 +57,6 @@ class BOption extends AbstractBeccaEntity {
this.isSynced = !!row.isSynced;
/** @type {string} */
this.utcDateModified = row.utcDateModified;
-
- this.becca.options[this.name] = this;
}
beforeSaving() {
@@ -89,7 +92,7 @@ module.exports = BOption;
diff --git a/docs/backend_api/becca_entities_brecent_note.js.html b/docs/backend_api/becca_entities_brecent_note.js.html
index 2e2f5ccb1..02f6858fb 100644
--- a/docs/backend_api/becca_entities_brecent_note.js.html
+++ b/docs/backend_api/becca_entities_brecent_note.js.html
@@ -77,7 +77,7 @@ module.exports = BRecentNote;
diff --git a/docs/backend_api/index.html b/docs/backend_api/index.html
index 55d778a7e..f9f7919b5 100644
--- a/docs/backend_api/index.html
+++ b/docs/backend_api/index.html
@@ -56,7 +56,7 @@
diff --git a/docs/backend_api/module-sql.html b/docs/backend_api/module-sql.html
index 6517bc0d4..2605d6530 100644
--- a/docs/backend_api/module-sql.html
+++ b/docs/backend_api/module-sql.html
@@ -1300,7 +1300,7 @@
diff --git a/docs/backend_api/services_backend_script_api.js.html b/docs/backend_api/services_backend_script_api.js.html
index 44f40bed6..bffce4bcd 100644
--- a/docs/backend_api/services_backend_script_api.js.html
+++ b/docs/backend_api/services_backend_script_api.js.html
@@ -165,13 +165,13 @@ function BackendScriptApi(currentNote, apiParams) {
this.getNoteWithLabel = attributeService.getNoteWithLabel;
/**
- * If there's no branch between note and parent note, create one. Otherwise, do nothing.
+ * If there's no branch between note and parent note, create one. Otherwise, do nothing. Returns the new or existing branch.
*
* @method
* @param {string} noteId
* @param {string} parentNoteId
* @param {string} prefix - if branch will be created between note and parent note, set this prefix
- * @returns {void}
+ * @returns {{branch: BBranch|null}}
*/
this.ensureNoteIsPresentInParent = cloningService.ensureNoteIsPresentInParent;
@@ -499,11 +499,11 @@ function BackendScriptApi(currentNote, apiParams) {
if (opts.type === 'script' && !opts.scriptNoteId) { throw new Error("scriptNoteId is mandatory for launchers of type 'script'"); }
if (opts.type === 'customWidget' && !opts.widgetNoteId) { throw new Error("widgetNoteId is mandatory for launchers of type 'customWidget'"); }
- const parentNoteId = !!opts.isVisible ? '_lbVisibleLaunchers' : '_lbAvailableLaunchers';
+ const parentNoteId = opts.isVisible ? '_lbVisibleLaunchers' : '_lbAvailableLaunchers';
const noteId = 'al_' + opts.id;
const launcherNote =
- becca.getNote(opts.id) ||
+ becca.getNote(noteId) ||
specialNotesService.createLauncher({
noteId: noteId,
parentNoteId: parentNoteId,
@@ -542,7 +542,7 @@ function BackendScriptApi(currentNote, apiParams) {
if (opts.icon) {
launcherNote.setLabel('iconClass', `bx ${opts.icon}`);
} else {
- launcherNote.removeLabel('keyboardShortcut');
+ launcherNote.removeLabel('iconClass');
}
return {note: launcherNote};
@@ -584,7 +584,7 @@ module.exports = BackendScriptApi;
diff --git a/docs/backend_api/services_sql.js.html b/docs/backend_api/services_sql.js.html
index ec2ac56e6..783bf8301 100644
--- a/docs/backend_api/services_sql.js.html
+++ b/docs/backend_api/services_sql.js.html
@@ -245,7 +245,7 @@ function wrap(query, func) {
// in these cases error should be simply ignored.
console.log(e.message);
- return null
+ return null;
}
throw e;
@@ -309,7 +309,7 @@ function fillParamList(paramIds, truncate = true) {
}
// doing it manually to avoid this showing up on the sloq query list
- const s = stmt(`INSERT INTO param_list VALUES ${paramIds.map(paramId => `(?)`).join(',')}`, paramIds);
+ const s = stmt(`INSERT INTO param_list VALUES ${paramIds.map(paramId => `(?)`).join(',')}`);
s.run(paramIds);
}
@@ -413,7 +413,7 @@ module.exports = {
diff --git a/package-lock.json b/package-lock.json
index 6055a636e..682c4f5a0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,7 +5,6 @@
"requires": true,
"packages": {
"": {
- "name": "trilium",
"version": "0.60.1-beta",
"hasInstallScript": true,
"license": "AGPL-3.0-only",
diff --git a/src/becca/entities/bnote.js b/src/becca/entities/bnote.js
index b8e58c740..0be9a3ab4 100644
--- a/src/becca/entities/bnote.js
+++ b/src/becca/entities/bnote.js
@@ -1192,7 +1192,7 @@ class BNote extends AbstractBeccaEntity {
/**
* @param {string} [hoistedNoteId='root']
- * @return {{isArchived: boolean, isInHoistedSubTree: boolean, notePath: string[], isHidden: boolean}[]}
+ * @return {Array<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array, isHidden: boolean}>}
*/
getSortedNotePathRecords(hoistedNoteId = 'root') {
const isHoistedRoot = hoistedNoteId === 'root';
diff --git a/src/public/app/entities/fnote.js b/src/public/app/entities/fnote.js
index eff96df81..d73f14de2 100644
--- a/src/public/app/entities/fnote.js
+++ b/src/public/app/entities/fnote.js
@@ -332,7 +332,7 @@ class FNote {
/**
* @param {string} [hoistedNoteId='root']
- * @return {{isArchived: boolean, isInHoistedSubTree: boolean, notePath: string[], isHidden: boolean}[]}
+ * @return {Array<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array, isHidden: boolean}>}
*/
getSortedNotePathRecords(hoistedNoteId = 'root') {
const isHoistedRoot = hoistedNoteId === 'root';
From a6ade790c64ad9ee68f63d53743e0f90c7898672 Mon Sep 17 00:00:00 2001
From: zadam
Date: Wed, 31 May 2023 14:35:08 +0200
Subject: [PATCH 17/40] script api docs
---
docs/frontend_api/FAttribute.html | 2 +-
docs/frontend_api/FBranch.html | 2 +-
docs/frontend_api/FNote.html | 888 +++++++++++++++++-
docs/frontend_api/FNoteComplement.html | 2 +-
docs/frontend_api/FrontendScriptApi.html | 520 +---------
docs/frontend_api/entities_fattribute.js.html | 2 +-
docs/frontend_api/entities_fbranch.js.html | 2 +-
docs/frontend_api/entities_fnote.js.html | 139 ++-
.../entities_fnote_complement.js.html | 2 +-
docs/frontend_api/index.html | 2 +-
.../services_frontend_script_api.js.html | 34 +-
11 files changed, 990 insertions(+), 605 deletions(-)
diff --git a/docs/frontend_api/FAttribute.html b/docs/frontend_api/FAttribute.html
index 641c3a737..901dc598c 100644
--- a/docs/frontend_api/FAttribute.html
+++ b/docs/frontend_api/FAttribute.html
@@ -850,7 +850,7 @@ and relation (representing named relationship between source and target note)
diff --git a/docs/frontend_api/FBranch.html b/docs/frontend_api/FBranch.html
index 284bc72d5..4ec45ea97 100644
--- a/docs/frontend_api/FBranch.html
+++ b/docs/frontend_api/FBranch.html
@@ -1062,7 +1062,7 @@ parents.
@@ -7832,7 +7400,7 @@ Typical use case is when new note has been created, we should wait until it is s
diff --git a/docs/frontend_api/entities_fattribute.js.html b/docs/frontend_api/entities_fattribute.js.html
index dffe972bc..a9cb49a5f 100644
--- a/docs/frontend_api/entities_fattribute.js.html
+++ b/docs/frontend_api/entities_fattribute.js.html
@@ -121,7 +121,7 @@ export default FAttribute;
diff --git a/docs/frontend_api/entities_fbranch.js.html b/docs/frontend_api/entities_fbranch.js.html
index 2b7ced98d..deae06846 100644
--- a/docs/frontend_api/entities_fbranch.js.html
+++ b/docs/frontend_api/entities_fbranch.js.html
@@ -105,7 +105,7 @@ export default FBranch;
diff --git a/docs/frontend_api/entities_fnote.js.html b/docs/frontend_api/entities_fnote.js.html
index a31f615a2..84616e2cb 100644
--- a/docs/frontend_api/entities_fnote.js.html
+++ b/docs/frontend_api/entities_fnote.js.html
@@ -101,7 +101,7 @@ class FNote {
this.mime = row.mime;
}
- addParent(parentNoteId, branchId) {
+ addParent(parentNoteId, branchId, sort = true) {
if (parentNoteId === 'none') {
return;
}
@@ -111,6 +111,10 @@ class FNote {
}
this.parentToBranch[parentNoteId] = branchId;
+
+ if (sort) {
+ this.sortParents();
+ }
}
addChild(childNoteId, branchId, sort = true) {
@@ -217,7 +221,7 @@ class FNote {
// will sort the parents so that non-search & non-archived are first and archived at the end
// this is done so that non-search & non-archived paths are always explored as first when looking for note path
- resortParents() {
+ sortParents() {
this.parents.sort((aNoteId, bNoteId) => {
const aBranchId = this.parentToBranch[aNoteId];
@@ -225,7 +229,7 @@ class FNote {
return 1;
}
- const aNote = this.froca.getNoteFromCache([aNoteId]);
+ const aNote = this.froca.getNoteFromCache(aNoteId);
if (aNote.isArchived || aNote.isHiddenCompletely()) {
return 1;
@@ -271,6 +275,11 @@ class FNote {
return this.__filterAttrs(this.__getCachedAttributes([]), type, name);
}
+ /**
+ * @param {string[]} path
+ * @return {FAttribute[]}
+ * @private
+ */
__getCachedAttributes(path) {
// notes/clones cannot form tree cycles, it is possible to create attribute inheritance cycle via templates
// when template instance is a parent of template itself
@@ -323,63 +332,49 @@ class FNote {
return this.noteId === 'root';
}
- getAllNotePaths(encounteredNoteIds = null) {
+ /**
+ * Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles)
+ *
+ * @returns {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path)
+ */
+ getAllNotePaths() {
if (this.noteId === 'root') {
return [['root']];
}
- if (!encounteredNoteIds) {
- encounteredNoteIds = new Set();
+ const parentNotes = this.getParentNotes().filter(note => note.type !== 'search');
+ let notePaths = [];
+
+ if (parentNotes.length === 1) { // optimization for most common case
+ notePaths = parentNotes[0].getAllNotePaths();
+ } else {
+ notePaths = parentNotes.flatMap(parentNote => parentNote.getAllNotePaths());
}
- encounteredNoteIds.add(this.noteId);
-
- const parentNotes = this.getParentNotes();
- let paths;
-
- if (parentNotes.length === 1) { // optimization for the most common case
- if (encounteredNoteIds.has(parentNotes[0].noteId)) {
- return [];
- }
- else {
- paths = parentNotes[0].getAllNotePaths(encounteredNoteIds);
- }
- }
- else {
- paths = [];
-
- for (const parentNote of parentNotes) {
- if (encounteredNoteIds.has(parentNote.noteId)) {
- continue;
- }
-
- const newSet = new Set(encounteredNoteIds);
-
- paths.push(...parentNote.getAllNotePaths(newSet));
- }
+ for (const notePath of notePaths) {
+ notePath.push(this.noteId);
}
- for (const path of paths) {
- path.push(this.noteId);
- }
-
- return paths;
+ return notePaths;
}
- getSortedNotePaths(hoistedNotePath = 'root') {
+ /**
+ * @param {string} [hoistedNoteId='root']
+ * @return {Array<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array<string>, isHidden: boolean}>}
+ */
+ getSortedNotePathRecords(hoistedNoteId = 'root') {
+ const isHoistedRoot = hoistedNoteId === 'root';
+
const notePaths = this.getAllNotePaths().map(path => ({
notePath: path,
- isInHoistedSubTree: path.includes(hoistedNotePath),
- isArchived: path.find(noteId => froca.notes[noteId].isArchived),
- isSearch: path.find(noteId => froca.notes[noteId].type === 'search'),
+ isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
+ isArchived: path.some(noteId => froca.notes[noteId].isArchived),
isHidden: path.includes('_hidden')
}));
notePaths.sort((a, b) => {
if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
return a.isInHoistedSubTree ? -1 : 1;
- } else if (a.isSearch !== b.isSearch) {
- return a.isSearch ? 1 : -1;
} else if (a.isArchived !== b.isArchived) {
return a.isArchived ? 1 : -1;
} else if (a.isHidden !== b.isHidden) {
@@ -392,6 +387,28 @@ class FNote {
return notePaths;
}
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string[]} array of noteIds constituting the particular note path
+ */
+ getBestNotePath(hoistedNoteId = 'root') {
+ return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath;
+ }
+
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string} serialized note path (e.g. 'root/a1h315/js725h')
+ */
+ getBestNotePathString(hoistedNoteId = 'root') {
+ const notePath = this.getBestNotePath(hoistedNoteId);
+
+ return notePath?.join("/");
+ }
+
/**
* @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree
*/
@@ -403,7 +420,7 @@ class FNote {
for (const parentNote of this.getParentNotes()) {
if (parentNote.noteId === 'root') {
return false;
- } else if (parentNote.noteId === '_hidden') {
+ } else if (parentNote.noteId === '_hidden' || parentNote.type === 'search') {
continue;
}
@@ -415,6 +432,13 @@ class FNote {
return true;
}
+ /**
+ * @param {FAttribute[]} attributes
+ * @param {string} type
+ * @param {string} name
+ * @return {FAttribute[]}
+ * @private
+ */
__filterAttrs(attributes, type, name) {
this.__validateTypeName(type, name);
@@ -551,7 +575,9 @@ class FNote {
* @returns {boolean} true if note has an attribute with given type and name (including inherited)
*/
hasAttribute(type, name) {
- return !!this.getAttribute(type, name);
+ const attributes = this.getAttributes();
+
+ return attributes.some(attr => attr.name === name && attr.type === type);
}
/**
@@ -619,6 +645,20 @@ class FNote {
*/
hasLabel(name) { return this.hasAttribute(LABEL, name); }
+ /**
+ * @param {string} name - label name
+ * @returns {boolean} true if label exists (including inherited) and does not have "false" value.
+ */
+ isLabelTruthy(name) {
+ const label = this.getLabel(name);
+
+ if (!label) {
+ return false;
+ }
+
+ return label && label.value !== 'false';
+ }
+
/**
* @param {string} name - relation name
* @returns {boolean} true if relation exists (excluding inherited)
@@ -730,7 +770,14 @@ class FNote {
});
// attrs are not resorted if position changes after initial load
- promotedAttrs.sort((a, b) => a.position < b.position ? -1 : 1);
+ promotedAttrs.sort((a, b) => {
+ if (a.noteId === b.noteId) {
+ return a.position < b.position ? -1 : 1;
+ } else {
+ // inherited promoted attributes should stay grouped: https://github.com/zadam/trilium/issues/3761
+ return a.noteId < b.noteId ? -1 : 1;
+ }
+ });
return promotedAttrs;
}
@@ -930,7 +977,7 @@ export default FNote;
diff --git a/docs/frontend_api/entities_fnote_complement.js.html b/docs/frontend_api/entities_fnote_complement.js.html
index 6ea35e95a..252ea6a2f 100644
--- a/docs/frontend_api/entities_fnote_complement.js.html
+++ b/docs/frontend_api/entities_fnote_complement.js.html
@@ -82,7 +82,7 @@ export default FNoteComplement;
diff --git a/docs/frontend_api/index.html b/docs/frontend_api/index.html
index 08a8eb159..6465f193a 100644
--- a/docs/frontend_api/index.html
+++ b/docs/frontend_api/index.html
@@ -56,7 +56,7 @@
diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html
index a326810c3..679f2e5eb 100644
--- a/docs/frontend_api/services_frontend_script_api.js.html
+++ b/docs/frontend_api/services_frontend_script_api.js.html
@@ -63,36 +63,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
/** @property {dayjs} day.js library for date manipulation. See {@link https://day.js.org} for documentation */
this.dayjs = dayjs;
- /**
- * @property {RightPanelWidget}
- * @deprecated use api.RightPanelWidget instead
- */
- this.CollapsibleWidget = RightPanelWidget;
-
/** @property {RightPanelWidget} */
this.RightPanelWidget = RightPanelWidget;
/** @property {NoteContextAwareWidget} */
this.NoteContextAwareWidget = NoteContextAwareWidget;
- /**
- * @property {NoteContextAwareWidget}
- * @deprecated use NoteContextAwareWidget instead
- */
- this.TabAwareWidget = NoteContextAwareWidget;
-
- /**
- * @property {NoteContextAwareWidget}
- * @deprecated use NoteContextAwareWidget instead
- */
- this.TabCachingWidget = NoteContextAwareWidget;
-
- /**
- * @property {NoteContextAwareWidget}
- * @deprecated use NoteContextAwareWidget instead
- */
- this.NoteContextCachingWidget = NoteContextAwareWidget;
-
/** @property {BasicWidget} */
this.BasicWidget = BasicWidget;
@@ -117,7 +93,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
await ws.waitForMaxKnownEntityChangeId();
await appContext.tabManager.getActiveContext().setNote(notePath);
- appContext.triggerEvent('focusAndSelectTitle');
+ await appContext.triggerEvent('focusAndSelectTitle');
};
/**
@@ -134,7 +110,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
await appContext.tabManager.openContextWithNote(notePath, { activate });
if (activate) {
- appContext.triggerEvent('focusAndSelectTitle');
+ await appContext.triggerEvent('focusAndSelectTitle');
}
};
@@ -152,10 +128,10 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
const {ntxId} = subContexts[subContexts.length - 1];
- appContext.triggerCommand("openNewNoteSplit", {ntxId, notePath});
+ await appContext.triggerCommand("openNewNoteSplit", {ntxId, notePath});
if (activate) {
- appContext.triggerEvent('focusAndSelectTitle');
+ await appContext.triggerEvent('focusAndSelectTitle');
}
};
@@ -581,7 +557,7 @@ export default FrontendScriptApi;
From d2d286a4ff708bfe9e6bf2c04935379b9fd0db68 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Wed, 31 May 2023 21:47:43 +0800
Subject: [PATCH 18/40] Show highlighted text in the left pane
---
src/public/app/layouts/desktop_layout.js | 4 +-
src/public/app/widgets/highlighted_text.js | 36 ++++-----
.../options/text_notes/highlighted_text.js | 81 +++++++++----------
3 files changed, 59 insertions(+), 62 deletions(-)
diff --git a/src/public/app/layouts/desktop_layout.js b/src/public/app/layouts/desktop_layout.js
index 3a9ec2930..0125e4ed1 100644
--- a/src/public/app/layouts/desktop_layout.js
+++ b/src/public/app/layouts/desktop_layout.js
@@ -44,7 +44,7 @@ import BacklinksWidget from "../widgets/floating_buttons/zpetne_odkazy.js";
import SharedInfoWidget from "../widgets/shared_info.js";
import FindWidget from "../widgets/find.js";
import TocWidget from "../widgets/toc.js";
-import HltWidget from "../widgets/highlighted_text.js";
+import HighlightTextWidget from "../widgets/highlighted_text.js";
import BulkActionsDialog from "../widgets/dialogs/bulk_actions.js";
import AboutDialog from "../widgets/dialogs/about.js";
import HelpDialog from "../widgets/dialogs/help.js";
@@ -182,7 +182,7 @@ export default class DesktopLayout {
)
.child(new RightPaneContainer()
.child(new TocWidget())
- .child(new HltWidget())
+ .child(new HighlightTextWidget())
.child(...this.customWidgets.get('right-pane'))
)
)
diff --git a/src/public/app/widgets/highlighted_text.js b/src/public/app/widgets/highlighted_text.js
index 3dbfc9b5c..43bc377ce 100644
--- a/src/public/app/widgets/highlighted_text.js
+++ b/src/public/app/widgets/highlighted_text.js
@@ -10,41 +10,41 @@ import RightPanelWidget from "./right_panel_widget.js";
import options from "../services/options.js";
import OnClickButtonWidget from "./buttons/onclick_button.js";
-const TPL = `
+const TPL = `
-
+
`;
-export default class HltWidget extends RightPanelWidget {
+export default class HighlightTextWidget extends RightPanelWidget {
constructor() {
super();
@@ -59,20 +59,20 @@ export default class HltWidget extends RightPanelWidget {
isEnabled() {
return super.isEnabled()
&& this.note.type === 'text'
- && !this.noteContext.viewScope.hltTemporarilyHidden
+ && !this.noteContext.viewScope.highlightedTextTemporarilyHidden
&& this.noteContext.viewScope.viewMode === 'default';
}
async doRenderBody() {
this.$body.empty().append($(TPL));
- this.$hlt = this.$body.find('.hlt');
- this.$body.find('.hlt-widget').append(this.closeHltButton.render());
+ this.$hlt = this.$body.find('.highlighted-text');
+ this.$body.find('.highlighted-text-widget').append(this.closeHltButton.render());
}
async refreshWithNote(note) {
- const hltLabel = note.getLabel('hlt');
+ const hltLabel = note.getLabel('hideHighlightWidget');
- if (hltLabel?.value === 'hide') {
+ if (hltLabel?.value=="" || hltLabel?.value=== "true") {
this.toggleInt(false);
this.triggerCommand("reEvaluateRightPaneVisibility");
return;
@@ -90,7 +90,7 @@ export default class HltWidget extends RightPanelWidget {
}
this.$hlt.html($hlt);
this.toggleInt(
- ["", "show"].includes(hltLabel?.value)
+ [undefined, "false"].includes(hltLabel?.value)
|| hltColors!=""
|| hltBgColors!=""
);
@@ -200,7 +200,7 @@ export default class HltWidget extends RightPanelWidget {
$li.css("display","none");
}
//The font color and background color may be nested or adjacent to each other. At this time, connect the front and back li to avoid interruption
- if(hltIndex!=0 && hltBCs[hltIndex-1].nextSibling ===hltBCs[hltIndex] && $hlt.children().last().css("display")!="none"){
+ if(hltIndex!=0 && hltBCs[hltIndex-1].nextSibling === hltBCs[hltIndex] && $hlt.children().last().css("display")!="none"){
$hlt.children().last().append($li.html());
}else{
$li.on("click", () => this.jumpToHlt(hltIndex));
@@ -233,7 +233,7 @@ export default class HltWidget extends RightPanelWidget {
}
async closeHltCommand() {
- this.noteContext.viewScope.hltTemporarilyHidden = true;
+ this.noteContext.viewScope.highlightedTextTemporarilyHidden = true;
await this.refresh();
this.triggerCommand('reEvaluateRightPaneVisibility');
}
@@ -255,13 +255,13 @@ class CloseHltButton extends OnClickButtonWidget {
super();
this.icon("bx-x")
- .title("Close HLT")
+ .title("Close HighlightTextWidget")
.titlePlacement("bottom")
.onClick((widget, e) => {
e.stopPropagation();
widget.triggerCommand("closeHlt");
})
- .class("icon-action close-hlt");
+ .class("icon-action close-highlighted-text");
}
}
diff --git a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
index 7fc2ce825..ec884959a 100644
--- a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
+++ b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
@@ -1,67 +1,64 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
-
+
Highlighted Text
- Displays highlighted text in the left pane. You can customize the highlighted text displayed in the left pane:
+ Displays highlighted text in the right panel. You can customize the highlighted text displayed in the right panel:
Text color:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Background color:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
export default class HighlightedTextOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
- this.$hltColors = this.$widget.find(".hlt-color");
+ this.$hltColors = this.$widget.find(".highlighted-text-color");
this.$hltColors.on('change', () => {
- const hltColorVals=this.$widget.find('input.hlt-color[type="checkbox"]:checked').map(function() {
+ const hltColorVals=this.$widget.find('input.highlighted-text-color[type="checkbox"]:checked').map(function() {
return this.value;
}).get();
this.updateOption('highlightedTextColors', JSON.stringify(hltColorVals));
});
- this.$hltBgColors = this.$widget.find(".hlt-background-color");
+ this.$hltBgColors = this.$widget.find(".highlighted-text-background-color");
this.$hltBgColors.on('change', () =>{
- const hltBgColorVals=this.$widget.find('input.hlt-background-color[type="checkbox"]:checked').map(function() {
+ const hltBgColorVals=this.$widget.find('input.highlighted-text-background-color[type="checkbox"]:checked').map(function() {
return this.value;
}).get();
this.updateOption('highlightedTextBgColors', JSON.stringify(hltBgColorVals));
@@ -72,14 +69,14 @@ export default class HighlightedTextOptions extends OptionsWidget {
async optionsLoaded(options) {
const hltColorVals=JSON.parse(options.highlightedTextColors);
const hltBgColorVals=JSON.parse(options.highlightedTextBgColors);
- this.$widget.find('input.hlt-color[type="checkbox"]').each(function () {
+ this.$widget.find('input.highlighted-text-color[type="checkbox"]').each(function () {
if ($.inArray($(this).val(), hltColorVals) !== -1) {
$(this).prop("checked", true);
} else {
$(this).prop("checked", false);
}
});
- this.$widget.find('input.hlt-background-color[type="checkbox"]').each(function () {
+ this.$widget.find('input.highlighted-text-background-color[type="checkbox"]').each(function () {
if ($.inArray($(this).val(), hltBgColorVals) !== -1) {
$(this).prop("checked", true);
} else {
From 92d5aeae41ea384c1b0249bd70721481bf7b4dfc Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Wed, 31 May 2023 22:34:24 +0800
Subject: [PATCH 19/40] Show highlighted text in the right panel
---
src/public/app/widgets/highlighted_text.js | 11 ++--
.../options/text_notes/highlighted_text.js | 62 +++++++++----------
src/services/options_init.js | 4 +-
3 files changed, 40 insertions(+), 37 deletions(-)
diff --git a/src/public/app/widgets/highlighted_text.js b/src/public/app/widgets/highlighted_text.js
index 43bc377ce..c9f3ef08c 100644
--- a/src/public/app/widgets/highlighted_text.js
+++ b/src/public/app/widgets/highlighted_text.js
@@ -82,11 +82,14 @@ export default class HighlightTextWidget extends RightPanelWidget {
let optionsHltColors = JSON.parse(options.get('highlightedTextColors'));
let optionsHltBgColors = JSON.parse(options.get('highlightedTextBgColors'));
+ const colorToValDic={"Dark": "#000000", "Dim grey": "#4d4d4d", "Grey": "#999999", "Light grey": "#e6e6e6", "White": "#ffffff", "Red": "#e64c4c", "Orange": "#e6994c", "Yellow": "#e6e64c", "Light green": "#99e64c", "Green": "#4ce64c", "Aquamarine": "#4ce699", "Turquoise": "#4ce6e6", "Light blue": "#4c99e6", "Blue": "#4c4ce6", "Purple": "#994ce6"}
+ const optionsHltColorsVal = optionsHltColors.map(color => colorToValDic[color]);
+ const optionsHltBgColorsVal = optionsHltBgColors.map(color => colorToValDic[color]);
// Check for type text unconditionally in case alwaysShowWidget is set
if (this.note.type === 'text') {
const { content } = await note.getNoteComplement();
//hltColors/hltBgColors are the colors/background-color that appear in notes and in options
- ({ $hlt, hltColors, hltBgColors } = await this.getHlt(content, optionsHltColors, optionsHltBgColors));
+ ({ $hlt, hltColors, hltBgColors } = await this.getHlt(content, optionsHltColorsVal, optionsHltBgColorsVal));
}
this.$hlt.html($hlt);
this.toggleInt(
@@ -167,7 +170,7 @@ export default class HighlightTextWidget extends RightPanelWidget {
/**
* Builds a jquery table of helight text.
*/
- getHlt(html, optionsHltColors, optionsHltBgColors) {
+ getHlt(html, optionsHltColorsVal, optionsHltBgColorsVal) {
const hltBCs = $(html).find(`span[style*="background-color"],span[style*="color"]`)
const $hlt = $("");
let hltColors = [];
@@ -181,7 +184,7 @@ export default class HighlightTextWidget extends RightPanelWidget {
if (color != "") {
var hexColor = this.colorToHex(color);
- if (this.hexIsInOptionHexs(hexColor,optionsHltColors)) {
+ if (this.hexIsInOptionHexs(hexColor,optionsHltColorsVal)) {
$li.html(hltText)
hltColors.push(hexColor);
liDisplay=true;
@@ -189,7 +192,7 @@ export default class HighlightTextWidget extends RightPanelWidget {
}
if (bgColor != "") {
var hexBgColor = this.colorToHex(bgColor);
- if (this.hexIsInOptionHexs(hexBgColor,optionsHltBgColors)) {
+ if (this.hexIsInOptionHexs(hexBgColor,optionsHltBgColorsVal)) {
//When you need to add a background color, in order to make the display more comfortable, change the background color to transparent
$li.html(hltText.css("background-color", hexBgColor+"80"))
hltBgColors.push(hexBgColor);
diff --git a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
index ec884959a..d5c284d94 100644
--- a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
+++ b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
@@ -3,7 +3,7 @@ import OptionsWidget from "../options_widget.js";
const TPL = `
@@ -105,12 +106,12 @@ export default class NoteInfoWidget extends NoteContextAwareWidget {
this.$subTreeSize.empty().append($(''));
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();
From c4f69fd9cbcc9dff08aa7cb2b38a1fc151d30e34 Mon Sep 17 00:00:00 2001
From: zadam
Date: Sat, 3 Jun 2023 00:21:46 +0200
Subject: [PATCH 32/40] don't allow patching relation's value in ETAPI #3998
---
src/etapi/attributes.js | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/etapi/attributes.js b/src/etapi/attributes.js
index 6886e0845..fb8b2ad99 100644
--- a/src/etapi/attributes.js
+++ b/src/etapi/attributes.js
@@ -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();
From d3bf325f19f9a45bf4a0559fdc11f6b8959b1d0c Mon Sep 17 00:00:00 2001
From: zadam
Date: Sat, 3 Jun 2023 00:32:16 +0200
Subject: [PATCH 33/40] added some comments to openapi spec
---
src/etapi/etapi.openapi.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/etapi/etapi.openapi.yaml b/src/etapi/etapi.openapi.yaml
index 974acbdfe..7c41693d1 100644
--- a/src/etapi/etapi.openapi.yaml
+++ b/src/etapi/etapi.openapi.yaml
@@ -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
From 1413756d0095d6b8384f297d78e1b35a9590de44 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Sat, 3 Jun 2023 11:55:50 +0800
Subject: [PATCH 34/40] Show highlighted text in right panel
---
src/public/app/widgets/highlighted_text.js | 168 ++++++++++--------
.../options/text_notes/highlighted_text.js | 83 ++-------
src/routes/api/options.js | 3 +-
src/services/options_init.js | 3 +-
4 files changed, 113 insertions(+), 144 deletions(-)
diff --git a/src/public/app/widgets/highlighted_text.js b/src/public/app/widgets/highlighted_text.js
index b6ced3124..f13bdc6b8 100644
--- a/src/public/app/widgets/highlighted_text.js
+++ b/src/public/app/widgets/highlighted_text.js
@@ -81,25 +81,20 @@ export default class HighlightTextWidget extends RightPanelWidget {
}
const hltLabel = note.getLabel('hideHighlightWidget');
- let optionsHltColors = JSON.parse(options.get('highlightedTextColors'));
- let optionsHltBgColors = JSON.parse(options.get('highlightedTextBgColors'));
+ const optionsHlt = JSON.parse(options.get('highlightedText'));
- if (hltLabel?.value == "" || hltLabel?.value === "true" || (optionsHltColors=="" && optionsHltBgColors=="")) {
+ if (hltLabel?.value == "" || hltLabel?.value === "true" || optionsHlt == "") {
this.toggleInt(false);
this.triggerCommand("reEvaluateRightPaneVisibility");
return;
}
let $hlt = "", hltLiCount = -1;
- //Obtained by `textEditor.config.get('fontColor.colors'), but this command can only be used in edit mode, so it is directly saved here
- const colorToValDic = { "Black": "hsl(0,0%,0%)", "Dim grey": "hsl(0,0%,30%)", "Grey": "hsl(0,0%,60%)", "Light grey": "hsl(0,0%,90%)", "White": "hsl(0,0%,100%)", "Red": "hsl(0,75%,60%)", "Orange": "hsl(30,75%,60%)", "Yellow": "hsl(60,75%,60%)", "Light green": "hsl(90,75%,60%)", "Green": "hsl(120,75%,60%)", "Aquamarine": "hsl(150,75%,60%)", "Turquoise": "hsl(180,75%,60%)", "Light blue": "hsl(210,75%,60%)", "Blue": "hsl(240,75%,60%)", "Purple": "hsl(270,75%,60%)" }
- const optionsHltColorsVal = optionsHltColors.map(color => colorToValDic[color]);
- const optionsHltBgColorsVal = optionsHltBgColors.map(color => colorToValDic[color]);
// Check for type text unconditionally in case alwaysShowWidget is set
if (this.note.type === 'text') {
const { content } = await note.getNoteComplement();
//hltColors/hltBgColors are the colors/background-color that appear in notes and in options
- ({ $hlt, hltLiCount } = await this.getHlt(content, optionsHltColorsVal, optionsHltBgColorsVal));
+ ({ $hlt, hltLiCount } = await this.getHlt(content, optionsHlt));
}
this.$hlt.html($hlt);
if ([undefined, "false"].includes(hltLabel?.value) && hltLiCount > 0) {
@@ -110,90 +105,115 @@ export default class HighlightTextWidget extends RightPanelWidget {
this.noteContext.viewScope.highlightedTextTemporarilyHiddenPrevious = false;
}
-
this.triggerCommand("reEvaluateRightPaneVisibility");
}
/**
* Builds a jquery table of helight text.
*/
- getHlt(html, optionsHltColorsVal, optionsHltBgColorsVal) {
- const hltTagsRegex = /]*(?:background-color|color):[^;>]+;[^>]*>(.*?)<\/span>/gi;
- let prevEndIndex = -1;
- let prevLiDisplay = false;
- const $hlt = $("");
- let hltLiCount = 0;
- for (let match = null, hltIndex = 0; ((match = hltTagsRegex.exec(html)) !== null); hltIndex++) {
- var spanHtml = match[0];
- const styleString = match[0].match(/style="(.*?)"/)[1];
- const text = match[1];
- const startIndex = match.index;
- const endIndex = hltTagsRegex.lastIndex - 1;
- var $li = $('
');
-
- const styles = styleString
- .split(';')
- .filter(item => item.includes('background-color') || item.includes('color'))
- .map(item => item.trim());
-
- for (let stylesIndex = 0; stylesIndex < styles.length; stylesIndex++) {
- var [color, colorVal] = styles[stylesIndex].split(':');
- colorVal = colorVal.replace(/\s+/g, '');
- if (color == "color" && optionsHltColorsVal.indexOf(colorVal) >= 0) {
- $li.html(spanHtml)
- hltLiCount++;
-
- }
- else if (color == "background-color" && optionsHltBgColorsVal.indexOf(colorVal) >= 0) {
-
- //When you need to add a background color, in order to make the display more comfortable, change the background color to Translucent
- const spanHtmlRegex = /background-color:\s*(hsl|rgb)\((\d{1,3}),(\d{1,3}%?),(\d{1,3}%?)\)/i;
- let spanHtmlMatch = spanHtml.match(spanHtmlRegex);
- if (spanHtmlMatch && spanHtmlMatch.length > 4) {
- let newColorValue = `${spanHtmlMatch[1]}a(${spanHtmlMatch[2]},${spanHtmlMatch[3]},${spanHtmlMatch[4]},0.5)`;
- spanHtml = spanHtml.replace(spanHtmlRegex, `background-color: ${newColorValue}`);
- }
- $li.html(spanHtml)
- hltLiCount++;
-
- } else {
- $li.css("display", "none");
- }
- }
- if ($li.css("display")!="none"){
- if (prevEndIndex != -1 && startIndex === prevEndIndex + 1 && prevLiDisplay == true) {
- $hlt.children().last().append($li.html());
- } else {
- if ($li.text().trim() == "") { $li.css("display", "none"); }
- $li.on("click", () => this.jumpToHlt(hltIndex));
- $hlt.append($li);
- }
- }
-
- prevEndIndex = endIndex;
- prevLiDisplay = $li.css("display")!="none";
+ getHlt(html, optionsHlt) {
+ // element priority: span>i>strong>u
+ // matches a span containing background-color
+ const regex1 = /]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
+ // matches a span containing color
+ const regex2 = /]*style\s*=\s*[^>]*[^-]color:[^>]*?>[\s\S]*?<\/span>/gi;
+ // match italics
+ const regex3 = /[\s\S]*?<\/i>/gi;
+ // match bold
+ const regex4 = /[\s\S]*?<\/strong>/gi;
+ // match underline
+ const regex5 = /[\s\S]*?<\/u>/g;
+ // Possible values in optionsHlt: '["bold","italic","underline","color","bgColor"]'
+ let findSubStr="", combinedRegexStr = "";
+ if (optionsHlt.indexOf("bgColor") >= 0){
+ findSubStr+=`,span[style*="background-color"]`;
+ combinedRegexStr+=`|${regex1.source}`;
}
+ if (optionsHlt.indexOf("color") >= 0){
+ findSubStr+=`,span[style*="color"]`;
+ combinedRegexStr+=`|${regex2.source}`;
+ }
+ if (optionsHlt.indexOf("italic") >= 0){
+ findSubStr+=`,i`;
+ combinedRegexStr+=`|${regex3.source}`;
+ }
+ if (optionsHlt.indexOf("bold") >= 0){
+ findSubStr+=`,strong`;
+ combinedRegexStr+=`|${regex4.source}`;
+ }
+ if (optionsHlt.indexOf("underline") >= 0){
+ findSubStr+=`,u`;
+ combinedRegexStr+=`|${regex5.source}`;
+ }
+
+ findSubStr = findSubStr.substring(1)
+ combinedRegexStr = `(` + combinedRegexStr.substring(1) + `)`;
+ const combinedRegex = new RegExp(combinedRegexStr, 'gi');
+ var $hlt = $("");
+ var hltLiCount = 0;
+ let prevEndIndex = -1;
+ for (let match = null, hltIndex = 0; ((match = combinedRegex.exec(html)) !== null); hltIndex++) {
+ var subHtml = match[0];
+ const startIndex = match.index;
+ const endIndex = combinedRegex.lastIndex;
+ hltLiCount++;
+ if (prevEndIndex != -1 && startIndex === prevEndIndex) {
+ $hlt.children().last().append(subHtml);
+ } else {
+ var $li = $('
');
+ $li.html(subHtml);
+ if ($li.text().trim() == "") { $li.css("display", "none"); }
+ $li.on("click", () => this.jumpToHlt(findSubStr,hltIndex));
+ $hlt.append($li);
+ }
+ prevEndIndex = endIndex;
+ }
+
return {
$hlt,
hltLiCount
};
}
-
- async jumpToHlt(hltIndex) {
+ async jumpToHlt(findSubStr,hltIndex) {
const isReadOnly = await this.noteContext.isReadOnly();
+ let targetElement;
if (isReadOnly) {
const $container = await this.noteContext.getContentElement();
- const hltElement = $container.find(`span[style*="background-color"],span[style*="color"]`)[hltIndex];
-
- if (hltElement != null) {
- hltElement.scrollIntoView({ behavior: "smooth", block: "center" });
- }
+ targetElement=$container.find(findSubStr).filter(function() {
+ if (findSubStr.indexOf("color")>=0 && findSubStr.indexOf("background-color")<0){
+ let color = this.style.color;
+ return $(this).prop('tagName')=="SPAN" && color==""?false:true;
+ }else{
+ return true;
+ }
+ }).filter(function() {
+ return $(this).parent(findSubStr).length === 0
+ && $(this).parent().parent(findSubStr).length === 0
+ && $(this).parent().parent().parent(findSubStr).length === 0
+ && $(this).parent().parent().parent().parent(findSubStr).length === 0;
+ })
} else {
const textEditor = await this.noteContext.getTextEditor();
- $(textEditor.editing.view.domRoots.values().next().value).find(`span[style*="background-color"],span[style*="color"]`)[hltIndex].scrollIntoView({
- behavior: "smooth", block: "center"
- });
+ targetElement=$(textEditor.editing.view.domRoots.values().next().value).find(findSubStr).filter(function() {
+ // When finding span[style*="color"] but not looking for span[style*="background-color"],
+ // the background-color error will be regarded as color, so it needs to be filtered
+ if (findSubStr.indexOf("color")>=0 && findSubStr.indexOf("background-color")<0){
+ let color = this.style.color;
+ return $(this).prop('tagName')=="SPAN" && color==""?false:true;
+ }else{
+ return true;
+ }
+ }).filter(function() {
+ //Need to filter out the child elements of the element that has been found
+ return $(this).parent(findSubStr).length === 0
+ && $(this).parent().parent(findSubStr).length === 0
+ && $(this).parent().parent().parent(findSubStr).length === 0
+ && $(this).parent().parent().parent().parent(findSubStr).length === 0;
+ })
}
+ targetElement[hltIndex].scrollIntoView({
+ behavior: "smooth", block: "center"
+ });
}
async closeHltCommand() {
diff --git a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
index 8af3503f8..90a47b616 100644
--- a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
+++ b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
@@ -1,83 +1,34 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
-
-
+
Highlighted Text
-
- You can customize the highlighted text displayed in the right panel:
- Text color:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Background color:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ You can customize the highlighted text displayed in the right panel:
+
+
+
+
+
+
.toc li {
cursor: pointer;
+ text-align: justify;
+ text-justify: distribute;
+ word-wrap: break-word;
+ hyphens: auto;
}
.toc li:hover {
From 446c41d020f0df9647af491c5b24e3e20e87631e Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Sat, 3 Jun 2023 17:00:15 +0800
Subject: [PATCH 36/40] Show highlighted text in right panel
---
src/public/app/layouts/desktop_layout.js | 4 ++--
src/public/app/widgets/highlighted_text.js | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/public/app/layouts/desktop_layout.js b/src/public/app/layouts/desktop_layout.js
index 0125e4ed1..17d569c4c 100644
--- a/src/public/app/layouts/desktop_layout.js
+++ b/src/public/app/layouts/desktop_layout.js
@@ -44,7 +44,7 @@ import BacklinksWidget from "../widgets/floating_buttons/zpetne_odkazy.js";
import SharedInfoWidget from "../widgets/shared_info.js";
import FindWidget from "../widgets/find.js";
import TocWidget from "../widgets/toc.js";
-import HighlightTextWidget from "../widgets/highlighted_text.js";
+import HighlightedTextWidget from "../widgets/highlighted_text.js";
import BulkActionsDialog from "../widgets/dialogs/bulk_actions.js";
import AboutDialog from "../widgets/dialogs/about.js";
import HelpDialog from "../widgets/dialogs/help.js";
@@ -182,7 +182,7 @@ export default class DesktopLayout {
)
.child(new RightPaneContainer()
.child(new TocWidget())
- .child(new HighlightTextWidget())
+ .child(new HighlightedTextWidget())
.child(...this.customWidgets.get('right-pane'))
)
)
diff --git a/src/public/app/widgets/highlighted_text.js b/src/public/app/widgets/highlighted_text.js
index f39e7c696..2ddecce88 100644
--- a/src/public/app/widgets/highlighted_text.js
+++ b/src/public/app/widgets/highlighted_text.js
@@ -46,7 +46,7 @@ const TPL = `
`;
-export default class HighlightTextWidget extends RightPanelWidget {
+export default class HighlightedTextWidget extends RightPanelWidget {
constructor() {
super();
@@ -242,7 +242,7 @@ class CloseHltButton extends OnClickButtonWidget {
super();
this.icon("bx-x")
- .title("Close HighlightTextWidget")
+ .title("Close HighlightedTextWidget")
.titlePlacement("bottom")
.onClick((widget, e) => {
e.stopPropagation();
From 3e3d7aa4d7e6cebadf7ec7083c11715e50c89992 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Sat, 3 Jun 2023 17:12:06 +0800
Subject: [PATCH 37/40] Show highlighted text in right panel
---
src/public/app/widgets/highlighted_text.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/public/app/widgets/highlighted_text.js b/src/public/app/widgets/highlighted_text.js
index 2ddecce88..b22697994 100644
--- a/src/public/app/widgets/highlighted_text.js
+++ b/src/public/app/widgets/highlighted_text.js
@@ -95,7 +95,6 @@ export default class HighlightedTextWidget extends RightPanelWidget {
// Check for type text unconditionally in case alwaysShowWidget is set
if (this.note.type === 'text') {
const { content } = await note.getNoteComplement();
- //hltColors/hltBgColors are the colors/background-color that appear in notes and in options
({ $hlt, hltLiCount } = await this.getHlt(content, optionsHlt));
}
this.$hlt.html($hlt);
From 282d135f0f1baac2f5be2b18ee073de3f20be50b Mon Sep 17 00:00:00 2001
From: dymani
Date: Sat, 3 Jun 2023 22:03:54 +0800
Subject: [PATCH 38/40] Fix shortcuts not resetting to default
---
src/public/app/widgets/type_widgets/options/shortcuts.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/public/app/widgets/type_widgets/options/shortcuts.js b/src/public/app/widgets/type_widgets/options/shortcuts.js
index a2c89437b..b37975b08 100644
--- a/src/public/app/widgets/type_widgets/options/shortcuts.js
+++ b/src/public/app/widgets/type_widgets/options/shortcuts.js
@@ -116,11 +116,11 @@ export default class KeyboardShortcutsOptions extends OptionsWidget {
return;
}
- $table.find('input.form-control').each(function() {
- const defaultShortcuts = this.$widget.find(this).attr('data-default-keyboard-shortcuts');
+ $table.find('input.form-control').each((_index, el) => {
+ const defaultShortcuts = this.$widget.find(el).attr('data-default-keyboard-shortcuts');
- if (this.$widget.find(this).val() !== defaultShortcuts) {
- this.$widget.find(this)
+ if (this.$widget.find(el).val() !== defaultShortcuts) {
+ this.$widget.find(el)
.val(defaultShortcuts)
.trigger('change');
}
From 8852e8e531062dcf92f30cb1ea8da57e3ac2502e Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Sun, 4 Jun 2023 16:02:30 +0800
Subject: [PATCH 39/40] Show highlighted text in right panel
---
src/public/app/widgets/highlighted_text.js | 14 ++++++++------
src/public/app/widgets/toc.js | 8 ++++----
.../options/text_notes/highlighted_text.js | 2 +-
3 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/src/public/app/widgets/highlighted_text.js b/src/public/app/widgets/highlighted_text.js
index b22697994..daced39d4 100644
--- a/src/public/app/widgets/highlighted_text.js
+++ b/src/public/app/widgets/highlighted_text.js
@@ -72,11 +72,11 @@ export default class HighlightedTextWidget extends RightPanelWidget {
}
async refreshWithNote(note) {
- /*The reason for adding highlightedTextTemporarilyHiddenPrevious is to record whether the previous state of the highlightedText is hidden or displayed,
+ /*The reason for adding highlightedTextPreviousVisible is to record whether the previous state of the highlightedText is hidden or displayed,
* and then let it be displayed/hidden at the initial time.
* If there is no such value, when the right panel needs to display toc but not highlighttext, every time the note content is changed,
* highlighttext Widget will appear and then close immediately, because getHlt function will consume time*/
- if (this.noteContext.viewScope.highlightedTextTemporarilyHiddenPrevious == true) {
+ if (this.noteContext.viewScope.highlightedTextPreviousVisible == true) {
this.toggleInt(true);
} else {
this.toggleInt(false);
@@ -100,20 +100,19 @@ export default class HighlightedTextWidget extends RightPanelWidget {
this.$hlt.html($hlt);
if ([undefined, "false"].includes(hltLabel?.value) && hltLiCount > 0) {
this.toggleInt(true);
- this.noteContext.viewScope.highlightedTextTemporarilyHiddenPrevious = true;
+ this.noteContext.viewScope.highlightedTextPreviousVisible = true;
} else {
this.toggleInt(false);
- this.noteContext.viewScope.highlightedTextTemporarilyHiddenPrevious = false;
+ this.noteContext.viewScope.highlightedTextPreviousVisible = false;
}
this.triggerCommand("reEvaluateRightPaneVisibility");
}
/**
- * Builds a jquery table of helight text.
+ * Builds a table of helight text.
*/
getHlt(html, optionsHlt) {
- // element priority: span>i>strong>u
// matches a span containing background-color
const regex1 = /]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
// matches a span containing color
@@ -125,6 +124,7 @@ export default class HighlightedTextWidget extends RightPanelWidget {
// match underline
const regex5 = /[\s\S]*?<\/u>/g;
// Possible values in optionsHlt: '["bold","italic","underline","color","bgColor"]'
+ // element priority: span>i>strong>u
let findSubStr="", combinedRegexStr = "";
if (optionsHlt.indexOf("bgColor") >= 0){
findSubStr+=`,span[style*="background-color"]`;
@@ -157,8 +157,10 @@ export default class HighlightedTextWidget extends RightPanelWidget {
const startIndex = match.index;
const endIndex = combinedRegex.lastIndex;
if (prevEndIndex != -1 && startIndex === prevEndIndex) {
+ //If the previous element is connected to this element in HTML, then concatenate them into one.
$hlt.children().last().append(subHtml);
} else {
+ //hide li if its text content is empty
if ([...subHtml.matchAll(/(?<=^|>)[^><]+?(?=<|$)/g)].map(matchTmp => matchTmp[0]).join('').trim() != ""){
var $li = $('
');
$li.html(subHtml);
diff --git a/src/public/app/widgets/toc.js b/src/public/app/widgets/toc.js
index 43ecb8f2d..8d66b3294 100644
--- a/src/public/app/widgets/toc.js
+++ b/src/public/app/widgets/toc.js
@@ -84,11 +84,11 @@ export default class TocWidget extends RightPanelWidget {
}
async refreshWithNote(note) {
- /*The reason for adding tocTemporarilyHiddenPrevious is to record whether the previous state of the toc is hidden or displayed,
+ /*The reason for adding tocPreviousVisible is to record whether the previous state of the toc is hidden or displayed,
* and then let it be displayed/hidden at the initial time. If there is no such value,
* when the right panel needs to display highlighttext but not toc, every time the note content is changed,
* toc will appear and then close immediately, because getToc(html) function will consume time*/
- if (this.noteContext.viewScope.tocTemporarilyHiddenPrevious ==true){
+ if (this.noteContext.viewScope.tocPreviousVisible ==true){
this.toggleInt(true);
}else{
this.toggleInt(false);
@@ -112,10 +112,10 @@ export default class TocWidget extends RightPanelWidget {
this.$toc.html($toc);
if (["", "show"].includes(tocLabel?.value) || headingCount >= options.getInt('minTocHeadings')){
this.toggleInt(true);
- this.noteContext.viewScope.tocTemporarilyHiddenPrevious=true;
+ this.noteContext.viewScope.tocPreviousVisible=true;
}else{
this.toggleInt(false);
- this.noteContext.viewScope.tocTemporarilyHiddenPrevious=false;
+ this.noteContext.viewScope.tocPreviousVisible=false;
}
this.triggerCommand("reEvaluateRightPaneVisibility");
diff --git a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
index 90a47b616..4f96999cc 100644
--- a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
+++ b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
@@ -16,7 +16,7 @@ const TPL = `
export default class HighlightedTextOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
- this.$hlt = this.$widget.find(".highlighted-text-check");
+ this.$hlt = this.$widget.find("input.highlighted-text-check");
this.$hlt.on('change', () => {
const hltVals=this.$widget.find('input.highlighted-text-check[type="checkbox"]:checked').map(function() {
return this.value;
From c177aaa901a9388bbce5341b3e6fdb41195dcbe2 Mon Sep 17 00:00:00 2001
From: zadam
Date: Sun, 4 Jun 2023 17:46:37 +0200
Subject: [PATCH 40/40] refactoring of highlight list
---
src/public/app/layouts/desktop_layout.js | 4 +-
...highlighted_text.js => highlights_list.js} | 174 +++++++++---------
.../options/text_notes/highlighted_text.js | 26 +--
3 files changed, 104 insertions(+), 100 deletions(-)
rename src/public/app/widgets/{highlighted_text.js => highlights_list.js} (51%)
diff --git a/src/public/app/layouts/desktop_layout.js b/src/public/app/layouts/desktop_layout.js
index e5c440cdc..cd5bb1912 100644
--- a/src/public/app/layouts/desktop_layout.js
+++ b/src/public/app/layouts/desktop_layout.js
@@ -44,7 +44,7 @@ import BacklinksWidget from "../widgets/floating_buttons/zpetne_odkazy.js";
import SharedInfoWidget from "../widgets/shared_info.js";
import FindWidget from "../widgets/find.js";
import TocWidget from "../widgets/toc.js";
-import HighlightedTextWidget from "../widgets/highlighted_text.js";
+import HighlightsListWidget from "../widgets/highlights_list.js";
import BulkActionsDialog from "../widgets/dialogs/bulk_actions.js";
import AboutDialog from "../widgets/dialogs/about.js";
import HelpDialog from "../widgets/dialogs/help.js";
@@ -185,7 +185,7 @@ export default class DesktopLayout {
)
.child(new RightPaneContainer()
.child(new TocWidget())
- .child(new HighlightedTextWidget())
+ .child(new HighlightsListWidget())
.child(...this.customWidgets.get('right-pane'))
)
)
diff --git a/src/public/app/widgets/highlighted_text.js b/src/public/app/widgets/highlights_list.js
similarity index 51%
rename from src/public/app/widgets/highlighted_text.js
rename to src/public/app/widgets/highlights_list.js
index daced39d4..4e88e9e45 100644
--- a/src/public/app/widgets/highlighted_text.js
+++ b/src/public/app/widgets/highlights_list.js
@@ -1,7 +1,7 @@
/**
* Widget: Show highlighted text in the right pane
*
- * By design there's no support for nonsensical or malformed constructs:
+ * By design, there's no support for nonsensical or malformed constructs:
* - For example, if there is a formula in the middle of the highlighted text, the two ends of the formula will be regarded as two entries
*/
@@ -10,20 +10,20 @@ import RightPanelWidget from "./right_panel_widget.js";
import options from "../services/options.js";
import OnClickButtonWidget from "./buttons/onclick_button.js";
-const TPL = `
+const TPL = `
-
+
`;
-export default class HighlightedTextWidget extends RightPanelWidget {
+export default class HighlightsListWidget extends RightPanelWidget {
constructor() {
super();
@@ -67,38 +67,38 @@ export default class HighlightedTextWidget extends RightPanelWidget {
async doRenderBody() {
this.$body.empty().append($(TPL));
- this.$hlt = this.$body.find('.highlighted-text');
- this.$body.find('.highlighted-text-widget').append(this.closeHltButton.render());
+ this.$highlightsList = this.$body.find('.highlists-list');
+ this.$body.find('.highlists-list-widget').append(this.closeHltButton.render());
}
async refreshWithNote(note) {
- /*The reason for adding highlightedTextPreviousVisible is to record whether the previous state of the highlightedText is hidden or displayed,
- * and then let it be displayed/hidden at the initial time.
- * If there is no such value, when the right panel needs to display toc but not highlighttext, every time the note content is changed,
- * highlighttext Widget will appear and then close immediately, because getHlt function will consume time*/
- if (this.noteContext.viewScope.highlightedTextPreviousVisible == true) {
+ /* The reason for adding highlightedTextPreviousVisible is to record whether the previous state
+ of the highlightedText is hidden or displayed, and then let it be displayed/hidden at the initial time.
+ If there is no such value, when the right panel needs to display toc but not highlighttext,
+ every time the note content is changed, highlighttext Widget will appear and then close immediately,
+ because getHlt function will consume time */
+ if (this.noteContext.viewScope.highlightedTextPreviousVisible) {
this.toggleInt(true);
} else {
this.toggleInt(false);
}
- const hltLabel = note.getLabel('hideHighlightWidget');
const optionsHlt = JSON.parse(options.get('highlightedText'));
- if (hltLabel?.value == "" || hltLabel?.value === "true" || optionsHlt == "") {
+ if (note.isLabelTruthy('hideHighlightWidget') || !optionsHlt) {
this.toggleInt(false);
this.triggerCommand("reEvaluateRightPaneVisibility");
return;
}
- let $hlt = "", hltLiCount = -1;
+ let $highlightsList = "", hltLiCount = -1;
// Check for type text unconditionally in case alwaysShowWidget is set
if (this.note.type === 'text') {
- const { content } = await note.getNoteComplement();
- ({ $hlt, hltLiCount } = await this.getHlt(content, optionsHlt));
+ const {content} = await note.getNoteComplement();
+ ({$highlightsList, hltLiCount} = this.getHighlightList(content, optionsHlt));
}
- this.$hlt.html($hlt);
- if ([undefined, "false"].includes(hltLabel?.value) && hltLiCount > 0) {
+ this.$highlightsList.empty().append($highlightsList);
+ if (hltLiCount > 0) {
this.toggleInt(true);
this.noteContext.viewScope.highlightedTextPreviousVisible = true;
} else {
@@ -109,12 +109,9 @@ export default class HighlightedTextWidget extends RightPanelWidget {
this.triggerCommand("reEvaluateRightPaneVisibility");
}
- /**
- * Builds a table of helight text.
- */
- getHlt(html, optionsHlt) {
+ getHighlightList(content, optionsHlt) {
// matches a span containing background-color
- const regex1 = /]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
+ const regex1 = /]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
// matches a span containing color
const regex2 = /]*style\s*=\s*[^>]*[^-]color:[^>]*?>[\s\S]*?<\/span>/gi;
// match italics
@@ -125,97 +122,103 @@ export default class HighlightedTextWidget extends RightPanelWidget {
const regex5 = /[\s\S]*?<\/u>/g;
// Possible values in optionsHlt: '["bold","italic","underline","color","bgColor"]'
// element priority: span>i>strong>u
- let findSubStr="", combinedRegexStr = "";
- if (optionsHlt.indexOf("bgColor") >= 0){
- findSubStr+=`,span[style*="background-color"]`;
- combinedRegexStr+=`|${regex1.source}`;
+ let findSubStr = "", combinedRegexStr = "";
+ if (optionsHlt.includes("bgColor")) {
+ findSubStr += `,span[style*="background-color"]`;
+ combinedRegexStr += `|${regex1.source}`;
}
- if (optionsHlt.indexOf("color") >= 0){
- findSubStr+=`,span[style*="color"]`;
- combinedRegexStr+=`|${regex2.source}`;
+ if (optionsHlt.includes("color")) {
+ findSubStr += `,span[style*="color"]`;
+ combinedRegexStr += `|${regex2.source}`;
}
- if (optionsHlt.indexOf("italic") >= 0){
- findSubStr+=`,i`;
- combinedRegexStr+=`|${regex3.source}`;
+ if (optionsHlt.includes("italic")) {
+ findSubStr += `,i`;
+ combinedRegexStr += `|${regex3.source}`;
}
- if (optionsHlt.indexOf("bold") >= 0){
- findSubStr+=`,strong`;
- combinedRegexStr+=`|${regex4.source}`;
+ if (optionsHlt.indexOf("bold")) {
+ findSubStr += `,strong`;
+ combinedRegexStr += `|${regex4.source}`;
}
- if (optionsHlt.indexOf("underline") >= 0){
- findSubStr+=`,u`;
- combinedRegexStr+=`|${regex5.source}`;
+ if (optionsHlt.includes("underline")) {
+ findSubStr += `,u`;
+ combinedRegexStr += `|${regex5.source}`;
}
findSubStr = findSubStr.substring(1)
combinedRegexStr = `(` + combinedRegexStr.substring(1) + `)`;
const combinedRegex = new RegExp(combinedRegexStr, 'gi');
- let $hlt = $("");
+ const $highlightsList = $("");
let prevEndIndex = -1, hltLiCount = 0;
- for (let match = null, hltIndex=0; ((match = combinedRegex.exec(html)) !== null); hltIndex++) {
- var subHtml = match[0];
+ for (let match = null, hltIndex = 0; ((match = combinedRegex.exec(content)) !== null); hltIndex++) {
+ const subHtml = match[0];
const startIndex = match.index;
const endIndex = combinedRegex.lastIndex;
- if (prevEndIndex != -1 && startIndex === prevEndIndex) {
- //If the previous element is connected to this element in HTML, then concatenate them into one.
- $hlt.children().last().append(subHtml);
+ if (prevEndIndex !== -1 && startIndex === prevEndIndex) {
+ // If the previous element is connected to this element in HTML, then concatenate them into one.
+ $highlightsList.children().last().append(subHtml);
} else {
- //hide li if its text content is empty
- if ([...subHtml.matchAll(/(?<=^|>)[^><]+?(?=<|$)/g)].map(matchTmp => matchTmp[0]).join('').trim() != ""){
- var $li = $('