diff --git a/README-ZH_CN.md b/README-ZH_CN.md
index a31caa2a1..47d93a3da 100644
--- a/README-ZH_CN.md
+++ b/README-ZH_CN.md
@@ -1,6 +1,6 @@
# Trilium Notes
-[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md)
+[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md) | [Japanese](https://github.com/zadam/trilium/blob/master/README.ja.md)
[](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Trilium Notes 是一个层次化的笔记应用程序,专注于建立大型个人知识库。请参阅[屏幕截图](https://github.com/zadam/trilium/wiki/Screenshot-tour)以快速了解:
diff --git a/README.ja.md b/README.ja.md
new file mode 100644
index 000000000..621e3e0ff
--- /dev/null
+++ b/README.ja.md
@@ -0,0 +1,83 @@
+# Trilium Notes
+
+[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md) | [Japanese](https://github.com/zadam/trilium/blob/master/README.ja.md)
+
+Trilium Notes は、大規模な個人知識ベースの構築に焦点を当てた、階層型ノートアプリケーションです。概要は[スクリーンショット](https://github.com/zadam/trilium/wiki/Screenshot-tour)をご覧ください:
+
+
+
+ウクライナは現在、ロシアの侵略から自国を守っています。[ウクライナ軍や人道的な慈善団体への寄付](https://standforukraine.com/)をご検討ください。
+
+
")
.addClass("dropdown-item")
.append($link)
+ .on('contextmenu', e => false)
// important to use mousedown instead of click since the former does not change focus
// (especially important for focused text for spell check)
.on('mousedown', e => {
e.stopPropagation();
+ if (e.which !== 1) { // only left click triggers menu items
+ return false;
+ }
+
this.hide();
if (item.handler) {
diff --git a/src/public/app/services/note_list_renderer.js b/src/public/app/services/note_list_renderer.js
index f0d9eb9f7..b22fdc827 100644
--- a/src/public/app/services/note_list_renderer.js
+++ b/src/public/app/services/note_list_renderer.js
@@ -12,86 +12,88 @@ const TPL = `
position: relative;
height: 100%;
}
-
+
.note-list.grid-view .note-list-container {
display: flex;
flex-wrap: wrap;
}
-
+
.note-list.grid-view .note-book-card {
flex-basis: 300px;
border: 1px solid transparent;
}
-
+
.note-list.grid-view .note-expander {
display: none;
}
-
+
.note-list.grid-view .note-book-card {
max-height: 300px;
}
-
+
.note-list.grid-view .note-book-card:hover {
cursor: pointer;
border: 1px solid var(--main-border-color);
background: var(--more-accented-background-color);
}
-
+
.note-book-card {
border-radius: 10px;
background-color: var(--accented-background-color);
padding: 10px 15px 15px 8px;
- margin: 5px 5px 5px 0;
+ margin: 5px 5px 5px 5px;
overflow: hidden;
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 1;
}
-
+
.note-book-card:not(.expanded) .note-book-content {
display: none !important;
padding: 10px
}
-
+
.note-book-card.expanded .note-book-content {
display: block;
min-height: 0;
height: 100%;
padding-top: 10px;
}
-
+
.note-book-header {
+ border-bottom: 1px solid var(--main-border-color);
margin-bottom: 0;
+ padding-bottom: .5rem;
word-break: break-all;
}
-
+
/* not-expanded title is limited to one line only */
.note-book-card:not(.expanded) .note-book-header {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
-
+
.note-book-header .rendered-note-attributes {
font-size: medium;
}
-
+
.note-book-header .rendered-note-attributes:before {
content: "\\00a0\\00a0";
}
-
+
.note-book-header .note-icon {
font-size: 100%;
display: inline-block;
padding-right: 7px;
position: relative;
}
-
+
.note-book-card .note-book-card {
border: 1px solid var(--main-border-color);
}
-
+
.note-book-content.type-image, .note-book-content.type-file, .note-book-content.type-protectedSession {
display: flex;
align-items: center;
@@ -99,46 +101,46 @@ const TPL = `
text-align: center;
padding: 10px;
}
-
+
.note-book-content.type-image img, .note-book-content.type-canvas svg {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
-
+
.note-book-card.type-image .note-book-content img,
.note-book-card.type-text .note-book-content img,
.note-book-card.type-canvas .note-book-content img {
max-width: 100%;
max-height: 100%;
}
-
+
.note-book-header {
flex-grow: 0;
}
-
+
.note-list-wrapper {
height: 100%;
overflow: auto;
}
-
+
.note-expander {
font-size: x-large;
position: relative;
top: 3px;
cursor: pointer;
}
-
+
.note-list-pager {
text-align: center;
}
-
+
`;
diff --git a/src/public/app/widgets/buttons/global_menu.js b/src/public/app/widgets/buttons/global_menu.js
index 9f01947ea..dc17cfbc5 100644
--- a/src/public/app/widgets/buttons/global_menu.js
+++ b/src/public/app/widgets/buttons/global_menu.js
@@ -16,16 +16,17 @@ const TPL = `
}
.global-menu-button {
- background-image: url("${window.glob.assetPath}/images/icon-black.png");
+ background-image: url("${window.glob.assetPath}/images/icon-black.svg");
background-repeat: no-repeat;
- background-position: 50% 45%;
+ background-position: 50% 80%;
+ background-size: 45px;
width: 100%;
height: 100%;
position: relative;
}
.global-menu-button:hover {
- background-image: url("${window.glob.assetPath}/images/icon-color.png");
+ background-image: url("${window.glob.assetPath}/images/icon-color.svg");
border: 0;
}
diff --git a/src/public/app/widgets/icon_list.js b/src/public/app/widgets/icon_list.js
index 1ad713b10..32fe21a95 100644
--- a/src/public/app/widgets/icon_list.js
+++ b/src/public/app/widgets/icon_list.js
@@ -11175,6 +11175,22 @@ const icons = [
}
];
+function getIconClass(icon) {
+ if (icon.type_of_icon === 'LOGO') {
+ return `bxl-${icon.name}`;
+ }
+ else if (icon.type_of_icon === 'SOLID') {
+ return `bxs-${icon.name}`;
+ }
+ else {
+ return `bx-${icon.name}`;
+ }
+}
+
+for (const icon of icons) {
+ icon.className = getIconClass(icon);
+}
+
export default {
categories,
icons
diff --git a/src/public/app/widgets/note_icon.js b/src/public/app/widgets/note_icon.js
index 20bdeed79..ba2bbb683 100644
--- a/src/public/app/widgets/note_icon.js
+++ b/src/public/app/widgets/note_icon.js
@@ -1,5 +1,6 @@
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import attributeService from "../services/attributes.js";
+import server from "../services/server.js";
const TPL = `
@@ -147,6 +148,8 @@ export default class NoteIconWidget extends NoteContextAwareWidget {
}
async renderDropdown(categoryId, search) {
+ const iconToCountPromise = this.getIconToCountMap();
+
this.$iconList.empty();
if (this.getIconLabels().length > 0) {
@@ -165,41 +168,50 @@ export default class NoteIconWidget extends NoteContextAwareWidget {
search = search?.trim()?.toLowerCase();
- for (const icon of icons) {
+ const filteredIcons = icons.filter(icon => {
if (categoryId && icon.category_id !== categoryId) {
- continue;
+ return false;
}
if (search) {
if (!icon.name.includes(search) && !icon.term?.find(t => t.includes(search))) {
- continue;
+ return false;
}
}
- this.$iconList.append(
- $('')
- .addClass(this.getIconClass(icon))
- .attr("title", icon.name)
- );
+ return true;
+ });
+
+ const iconToCount = await iconToCountPromise;
+
+ filteredIcons.sort((a, b) => {
+ const countA = iconToCount[a.className] || 0;
+ const countB = iconToCount[b.className] || 0;
+
+ return countB - countA;
+ });
+
+ for (const icon of filteredIcons) {
+ this.$iconList.append(this.renderIcon(icon));
}
this.$iconSearch.focus();
}
+ async getIconToCountMap() {
+ const {iconClassToCountMap} = await server.get('other/icon-usage');
+
+ return iconClassToCountMap;
+ }
+
+ renderIcon(icon) {
+ return $('')
+ .addClass("bx " + icon.className)
+ .attr("title", icon.name);
+ }
+
getIconLabels() {
return this.note.getOwnedLabels()
.filter(label => ['workspaceIconClass', 'iconClass'].includes(label.name));
}
-
- getIconClass(icon) {
- if (icon.type_of_icon === 'LOGO') {
- return `bx bxl-${icon.name}`;
- }
- else if (icon.type_of_icon === 'SOLID') {
- return `bx bxs-${icon.name}`;
- }
- else {
- return `bx bx-${icon.name}`;
- }
- }
}
diff --git a/src/public/app/widgets/note_tree.js b/src/public/app/widgets/note_tree.js
index ed6c03bfe..fe090136e 100644
--- a/src/public/app/widgets/note_tree.js
+++ b/src/public/app/widgets/note_tree.js
@@ -41,7 +41,6 @@ const TPL = `
}
.tree-actions {
- padding: 4px 0;
background-color: var(--launcher-pane-background-color);
z-index: 100;
position: absolute;
@@ -49,13 +48,15 @@ const TPL = `
display: flex;
align-items: flex-end;
justify-content: flex-end;
- right: 11.77px;
+ right: 17px;
+ border-radius: 7px;
+ border: 1px solid var(--main-border-color);
}
button.tree-floating-button {
+ margin: 1px;
font-size: 1.5em;
padding: 5px;
- margin-right: 5px;
max-height: 34px;
color: var(--launcher-pane-text-color);
background-color: var(--button-background-color);
diff --git a/src/public/app/widgets/ribbon_widgets/inherited_attribute_list.js b/src/public/app/widgets/ribbon_widgets/inherited_attribute_list.js
index e434c20a6..01031686e 100644
--- a/src/public/app/widgets/ribbon_widgets/inherited_attribute_list.js
+++ b/src/public/app/widgets/ribbon_widgets/inherited_attribute_list.js
@@ -92,7 +92,18 @@ export default class InheritedAttributesWidget extends NoteContextAwareWidget {
}
getInheritedAttributes(note) {
- return note.getAttributes().filter(attr => attr.noteId !== this.noteId);
+ const attrs = note.getAttributes().filter(attr => attr.noteId !== this.noteId);
+
+ attrs.sort((a, b) => {
+ if (a.noteId === b.noteId) {
+ return a.position < b.position ? -1 : 1;
+ } else {
+ // inherited attributes should stay grouped: https://github.com/zadam/trilium/issues/3761
+ return a.noteId < b.noteId ? -1 : 1;
+ }
+ });
+
+ return attrs;
}
entitiesReloadedEvent({loadResults}) {
diff --git a/src/public/app/widgets/sql_result.js b/src/public/app/widgets/sql_result.js
index 142dc79e3..4224e714b 100644
--- a/src/public/app/widgets/sql_result.js
+++ b/src/public/app/widgets/sql_result.js
@@ -60,7 +60,7 @@ export default class SqlResultWidget extends NoteContextAwareWidget {
const $row = $("");
for (const key in result) {
- $row.append($("").html(key));
+ $row.append($(" | ").text(key));
}
$table.append($row);
@@ -69,7 +69,7 @@ export default class SqlResultWidget extends NoteContextAwareWidget {
const $row = $(" |
");
for (const key in result) {
- $row.append($("").html(result[key]));
+ $row.append($(" | ").text(result[key]));
}
$table.append($row);
diff --git a/src/public/app/widgets/type_widgets/relation_map.js b/src/public/app/widgets/type_widgets/relation_map.js
index aa9d68a06..696ad250d 100644
--- a/src/public/app/widgets/type_widgets/relation_map.js
+++ b/src/public/app/widgets/type_widgets/relation_map.js
@@ -470,11 +470,7 @@ export default class RelationMapTypeWidget extends TypeWidget {
async createNoteBox(noteId, title, x, y) {
const $link = await linkService.createNoteLink(noteId, {title});
- $link.mousedown(e => {
- console.log(e);
-
- linkService.goToLink(e);
- });
+ $link.mousedown(e => linkService.goToLink(e));
const note = await froca.getNote(noteId);
diff --git a/src/public/stylesheets/theme-dark.css b/src/public/stylesheets/theme-dark.css
index 56e303359..542a79e79 100644
--- a/src/public/stylesheets/theme-dark.css
+++ b/src/public/stylesheets/theme-dark.css
@@ -73,7 +73,7 @@
}
body .global-menu-button {
- background-image: url("../images/icon-grey.png");
+ background-image: url("../images/icon-grey.svg");
}
body ::-webkit-calendar-picker-indicator {
diff --git a/src/routes/api/other.js b/src/routes/api/other.js
new file mode 100644
index 000000000..c7e00235b
--- /dev/null
+++ b/src/routes/api/other.js
@@ -0,0 +1,29 @@
+const becca = require("../../becca/becca");
+
+function getIconUsage() {
+ const iconClassToCountMap = {};
+
+ for (const {value: iconClass, noteId} of becca.findAttributes('label', 'iconClass')) {
+ if (noteId.startsWith("_")) {
+ continue; // ignore icons of "system" notes since they were not set by the user
+ }
+
+ if (!iconClass?.trim()) {
+ continue;
+ }
+
+ for (const clazz of iconClass.trim().split(/\s+/)) {
+ if (clazz === 'bx') {
+ continue;
+ }
+
+ iconClassToCountMap[clazz] = (iconClassToCountMap[clazz] || 0) + 1;
+ }
+ }
+
+ return { iconClassToCountMap };
+}
+
+module.exports = {
+ getIconUsage
+};
diff --git a/src/routes/routes.js b/src/routes/routes.js
index 035d16775..d1b0bc58f 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -57,6 +57,7 @@ const backendLogRoute = require('./api/backend_log');
const statsRoute = require('./api/stats');
const fontsRoute = require('./api/fonts');
const etapiTokensApiRoutes = require('./api/etapi_tokens');
+const otherRoute = require('./api/other');
const shareRoutes = require('../share/routes');
const etapiAuthRoutes = require('../etapi/auth');
const etapiAppInfoRoutes = require('../etapi/app_info');
@@ -305,6 +306,7 @@ function register(app) {
apiRoute(POST, '/api/delete-notes-preview', notesApiRoute.getDeleteNotesPreview);
route(GET, '/api/fonts', [auth.checkApiAuthOrElectron], fontsRoute.getFontCss);
+ apiRoute(GET, '/api/other/icon-usage', otherRoute.getIconUsage);
apiRoute(GET, '/api/etapi-tokens', etapiTokensApiRoutes.getTokens);
apiRoute(POST, '/api/etapi-tokens', etapiTokensApiRoutes.createToken);
diff --git a/src/services/build.js b/src/services/build.js
index 8b4a403e6..b8402c31e 100644
--- a/src/services/build.js
+++ b/src/services/build.js
@@ -1 +1 @@
-module.exports = { buildDate:"2023-03-14T21:15:08+01:00", buildRevision: "d8e9086bdeb721db795783b5d92395a9bd6882c9" };
+module.exports = { buildDate:"", buildRevision: "9881e6de3e4966af39ec6245562dca6ac7b25eaa" };
diff --git a/src/services/search/services/search.js b/src/services/search/services/search.js
index c633d4189..1aa360e5a 100644
--- a/src/services/search/services/search.js
+++ b/src/services/search/services/search.js
@@ -299,7 +299,9 @@ function highlightSearchResults(searchResults, highlightedTokens) {
// which would make the resulting HTML string invalid.
// { and } are used for marking and tag (to avoid matches on single 'b' character)
// < and > are used for marking and
- highlightedTokens = highlightedTokens.map(token => token.replace('/[<\{\}]/g', ''));
+ highlightedTokens = highlightedTokens
+ .map(token => token.replace('/[<\{\}]/g', ''))
+ .filter(token => !!token?.trim());
// sort by the longest, so we first highlight the longest matches
highlightedTokens.sort((a, b) => a.length > b.length ? -1 : 1);
@@ -307,7 +309,7 @@ function highlightSearchResults(searchResults, highlightedTokens) {
for (const result of searchResults) {
const note = becca.notes[result.noteId];
- result.highlightedNotePathTitle = result.notePathTitle.replace('/[<\{\}]/g', '');
+ result.highlightedNotePathTitle = result.notePathTitle.replace(/[<{}]/g, '');
if (highlightedTokens.find(token => note.type.includes(token))) {
result.highlightedNotePathTitle += ` "type: ${note.type}'`;
|