Merge remote-tracking branch 'origin/develop' into feature/native_window_buttons

; Conflicts:
;	src/public/stylesheets/theme-next.css
This commit is contained in:
Elian Doran 2024-12-06 21:23:49 +02:00
commit 8cf3addb7d
No known key found for this signature in database
23 changed files with 680 additions and 96 deletions

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -258,9 +258,14 @@ function goToLinkExt(evt, hrefLink, $link) {
} else {
// Enable protocols supported by CKEditor 5 to be clickable.
// Refer to `allowedProtocols` in https://github.com/TriliumNext/trilium-ckeditor5/blob/main/packages/ckeditor5-build-balloon-block/src/ckeditor.ts.
// Adding `:` to these links might be safer.
const otherAllowedProtocols = ['mailto:', 'tel:', 'sms:', 'sftp:', 'smb:', 'slack:', 'zotero:'];
if (otherAllowedProtocols.some(protocol => hrefLink.toLowerCase().startsWith(protocol))){
// And be consistent with `allowedSchemes` in `src\services\html_sanitizer.ts`
const allowedSchemes = [
'http', 'https', 'ftp', 'ftps', 'mailto', 'data', 'evernote', 'file', 'facetime', 'gemini', 'git',
'gopher', 'imap', 'irc', 'irc6', 'jabber', 'jar', 'lastfm', 'ldap', 'ldaps', 'magnet', 'message',
'mumble', 'nfs', 'onenote', 'pop', 'rmi', 's3', 'sftp', 'skype', 'sms', 'spotify', 'steam', 'svn', 'udp',
'view-source', 'vlc', 'vnc', 'ws', 'wss', 'xmpp', 'jdbc', 'slack', 'tel', 'smb', 'zotero'
];
if (allowedSchemes.some(protocol => hrefLink.toLowerCase().startsWith(protocol+':'))){
window.open(hrefLink, '_blank');
}
}

View File

@ -273,10 +273,23 @@ async function getNoteTitleWithPathAsSuffix(notePath) {
.append($('<span class="note-title">').text(title));
if (path.length > 0) {
$titleWithPath
.append($('<span class="note-path">').text(` (${path.join(' / ')})`));
}
const $notePath = $('<span class="note-path">');
$notePath.append($(`<span class="path-bracket"> (</span>)`));
for (let segmentIndex = 0; segmentIndex < path.length; segmentIndex++) {
$notePath.append($(`<span>`).text(path[segmentIndex]));
if (segmentIndex < path.length - 1) {
$notePath.append($(`<span class="path-delimiter">`).text(" / "));
}
}
$notePath.append($(`<span class="path-bracket">)</span>)`));
$titleWithPath.append($notePath);
}
return $titleWithPath;
}

View File

@ -33,21 +33,21 @@ const DROPDOWN_TPL = `
<div class="calendar-header">
<div class="calendar-month-selector">
<button class="calendar-btn bx bx-left-arrow-alt" data-calendar-toggle="previous"></button>
<button class="calendar-btn bx bx-chevron-left" data-calendar-toggle="previous"></button>
<select data-calendar-input="month">
${Object.entries(MONTHS).map(([i, month]) => `<option value=${i}>${month}</option>`)}
</select>
<button class="calendar-btn bx bx-right-arrow-alt" data-calendar-toggle="next"></button>
<button class="calendar-btn bx bx-chevron-right" data-calendar-toggle="next"></button>
</div>
<div class="calendar-year-selector">
<button class="calendar-btn bx bx-left-arrow-alt" data-calendar-toggle="previousYear"></button>
<button class="calendar-btn bx bx-chevron-left" data-calendar-toggle="previousYear"></button>
<input type="number" min="1900" max="2999" step="1" data-calendar-input="year" />
<button class="calendar-btn bx bx-right-arrow-alt" data-calendar-toggle="nextYear"></button>
<button class="calendar-btn bx bx-chevron-right" data-calendar-toggle="nextYear"></button>
</div>
</div>

View File

@ -49,7 +49,7 @@ const TPL = `
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="radio" name="export-subtree-format" value="html">
${t('export.format_html')}
${t('export.format_html_zip')}
</label>
</div>

View File

@ -28,6 +28,51 @@ const TPL = `<div class="note-map-widget" style="position: relative;">
font-size: 130%;
padding: 1px 10px 1px 10px;
}
/* Style Ui Element to Drag Nodes */
.fixnodes-type-switcher {
position: absolute;
top: 10px;
left: 45%;
z-index: 10; /* should be below dropdown (note actions) */
border-radius:0.2rem;
}
/* Start of styling the slider */
input[type="range"] {
/* removing default appearance */
-webkit-appearance: none;
appearance: none;
margin-left: 15px;
width:50%
}
/* Changing slider tracker */
input[type="range"]::-webkit-slider-runnable-track {
height: 6px;
background: #ccc;
border-radius: 16px;
}
/* Changing Slider Thumb*/
input[type="range"]::-webkit-slider-thumb {
/* removing default appearance */
-webkit-appearance: none;
appearance: none;
/* creating a custom design */
height: 15px;
width: 15px;
margin-top:-4px;
background-color: #661822;
border-radius: 50%;
/* End of styling the slider */
</style>
<div class="btn-group btn-group-sm map-type-switcher" role="group">
@ -35,6 +80,14 @@ const TPL = `<div class="note-map-widget" style="position: relative;">
<button type="button" class="btn bx bx-sitemap" title="${t("note-map.button-tree-map")}" data-type="tree"></button>
</div>
<! UI for dragging Notes and link force >
<div class=" btn-group-sm fixnodes-type-switcher" role="group">
<button type="button" class="btn bx bx-expand" title="Fixation" data-type="moveable"></button>
<input type="range" class=" slider" min="1" title="Link distance" max="100" value="40" >
</div>
<div class="style-resolver"></div>
<div class="note-map-container"></div>
@ -43,7 +96,7 @@ const TPL = `<div class="note-map-widget" style="position: relative;">
export default class NoteMapWidget extends NoteContextAwareWidget {
constructor(widgetMode) {
super();
this.fixNodes = false; // needed to save the status of the UI element. Is set later in the code
this.widgetMode = widgetMode; // 'type' or 'ribbon'
}
@ -64,6 +117,13 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
await attributeService.setLabel(this.noteId, 'mapType', type);
});
// Reading the status of the Drag nodes Ui element. Changing it´s color when activated. Reading Force value of the link distance.
this.$widget.find('.fixnodes-type-switcher').on('click', async event => {
this.fixNodes = !this.fixNodes;
event.target.style.backgroundColor = this.fixNodes ? '#661822' : 'transparent';
});
super.doRender();
}
@ -92,13 +152,61 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
await libraryLoader.requireLibrary(libraryLoader.FORCE_GRAPH);
//variables for the hover effekt. We have to save the neighbours of a hovered node in a set. Also we need to save the links as well as the hovered node itself
let hoverNode = null;
const highlightLinks = new Set();
const neighbours = new Set();
this.graph = ForceGraph()(this.$container[0])
.width(this.$container.width())
.height(this.$container.height())
.onZoom(zoom => this.setZoomLevel(zoom.k))
.d3AlphaDecay(0.01)
.d3VelocityDecay(0.08)
.nodeCanvasObject((node, ctx) => this.paintNode(node, this.getColorForNode(node), ctx))
//Code to fixate nodes when dragged
.onNodeDragEnd(node => {
if (this.fixNodes) {
node.fx = node.x;
node.fy = node.y;
} else {
node.fx = null;
node.fy = null;
}
})
//check if hovered and set the hovernode variable, saving the hovered node object into it. Clear links variable everytime you hover. Without clearing links will stay highlighted
.onNodeHover(node => {
hoverNode = node || null;
highlightLinks.clear();
})
// set link width to immitate a highlight effekt. Checking the condition if any links are saved in the previous defined set highlightlinks
.linkWidth(link => (highlightLinks.has(link) ? 3 : 0.4))
.linkColor(link => (highlightLinks.has(link) ? 'white' : this.css.mutedTextColor))
.linkDirectionalArrowLength(4)
.linkDirectionalArrowRelPos(0.95)
// main code for highlighting hovered nodes and neighbours. here we "style" the nodes. the nodes are rendered several hundred times per second.
.nodeCanvasObject((node, ctx) => {
if (hoverNode == node) { //paint only hovered node
this.paintNode(node, '#661822', ctx);
neighbours.clear(); //clearing neighbours or the effect would be maintained after hovering is over
for (const link of data.links) { //check if node is part of a link in the canvas, if so add it´s neighbours and related links to the previous defined variables to paint the nodes
if (link.source.id == node.id || link.target.id == node.id) {
neighbours.add(link.source);
neighbours.add(link.target);
highlightLinks.add(link);
neighbours.delete(node);
}
}
} else if (neighbours.has(node) && hoverNode != null) { //paint neighbours
this.paintNode(node, '#9d6363', ctx);
} else {
this.paintNode(node, this.getColorForNode(node), ctx); //paint rest of nodes in canvas
}
})
.nodePointerAreaPaint((node, ctx) => this.paintNode(node, this.getColorForNode(node), ctx))
.nodePointerAreaPaint((node, color, ctx) => {
ctx.fillStyle = color;
@ -109,10 +217,6 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
.nodeLabel(node => esc(node.name))
.maxZoom(7)
.warmupTicks(30)
.linkDirectionalArrowLength(5)
.linkDirectionalArrowRelPos(1)
.linkWidth(1)
.linkColor(() => this.css.mutedTextColor)
.onNodeClick(node => appContext.tabManager.getActiveContext().setNote(node.id))
.onNodeRightClick((node, e) => linkContextMenuService.openContextMenu(node.id, e));
@ -130,8 +234,15 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
const magnifiedRatio = Math.pow(nodeLinkRatio, 1.5);
const charge = -20 / magnifiedRatio;
const boundedCharge = Math.min(-3, charge);
let distancevalue = 40; // default value for the link force of the nodes
this.$widget.find('.fixnodes-type-switcher input').on('change', async e => {
distancevalue = e.target.closest('input').value;
this.graph.d3Force('link').distance(distancevalue);
this.renderData(data);
});
this.graph.d3Force('link').distance(40);
this.graph.d3Force('center').strength(0.2);
this.graph.d3Force('charge').strength(boundedCharge);
this.graph.d3Force('charge').distanceMax(1000);
@ -201,7 +312,7 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, size, 0, 2 * Math.PI, false);
ctx.arc(x, y, size*0.8, 0, 2 * Math.PI, false);
ctx.fill();
const toRender = this.zoomLevel > 2

View File

@ -43,7 +43,7 @@ export default class NoteWrapperWidget extends FlexContainer {
}
this.$widget.toggleClass("full-content-width",
['image', 'mermaid', 'book', 'render', 'canvas', 'webView'].includes(note.type)
['image', 'mermaid', 'book', 'render', 'canvas', 'webView', 'mindMap'].includes(note.type)
|| !!note?.isLabelTruthy('fullContentWidth')
);

View File

@ -146,7 +146,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
async createPromotedAttributeCell(definitionAttr, valueAttr, valueName) {
const definition = definitionAttr.getDefinition();
const id = `value-${this.noteId}-${definitionAttr.position}`;
const id = `value-${valueAttr.attributeId}`;
const $input = $("<input>")
.prop("tabindex", 200 + definitionAttr.position)

View File

@ -139,13 +139,14 @@ const TAB_ROW_TPL = `
overflow: hidden;
pointer-events: all;
color: var(--inactive-tab-text-color);
background-color: var(--inactive-tab-background-color);
--tab-background-color: var(--workspace-tab-background-color);
background-color: var(--tab-background-color, var(--inactive-tab-background-color));
}
.tab-row-widget .note-tab[active] .note-tab-wrapper {
font-weight: bold;
color: var(--active-tab-text-color);
background-color : var(--active-tab-background-color);
background-color : var(--tab-background-color, var(--active-tab-background-color));
}
.tab-row-widget .note-tab[is-mini] .note-tab-wrapper {
@ -189,11 +190,11 @@ const TAB_ROW_TPL = `
}
.tab-row-widget .note-tab:hover .note-tab-wrapper {
background-color: var(--inactive-tab-hover-background-color);
background-color: var(--tab-background-color, var(--inactive-tab-hover-background-color));
}
.tab-row-widget .note-tab[active]:hover .note-tab-wrapper {
background-color: var(--active-tab-hover-background-color);
background-color: var(--tab-background-color, var(--active-tab-hover-background-color));
}
.tab-row-widget .note-tab .note-tab-close:hover {
@ -243,6 +244,9 @@ const TAB_ROW_TPL = `
export default class TabRowWidget extends BasicWidget {
doRender() {
this.$widget = $(TAB_ROW_TPL);
const documentStyle = window.getComputedStyle(document.documentElement);
this.showNoteIcons = (documentStyle.getPropertyValue("--tab-note-icons") === "true");
this.draggabillies = [];
@ -667,18 +671,18 @@ export default class TabRowWidget extends BasicWidget {
}
}
let noteIcon = "";
if (noteContext) {
const hoistedNote = froca.getNoteFromCache(noteContext.hoistedNoteId);
if (hoistedNote) {
$tab.find('.note-tab-icon')
.removeClass()
.addClass("note-tab-icon")
.addClass(hoistedNote.getWorkspaceIconClass());
$tab.find('.note-tab-wrapper').css("background", hoistedNote.getWorkspaceTabBackgroundColor());
}
else {
$tab.find('.note-tab-wrapper')
.css("--workspace-tab-background-color", hoistedNote.getWorkspaceTabBackgroundColor());
if (!this.showNoteIcons) {
noteIcon = hoistedNote.getWorkspaceIconClass();
}
} else {
$tab.find('.note-tab-wrapper').removeAttr("style");
}
}
@ -688,7 +692,7 @@ export default class TabRowWidget extends BasicWidget {
if (!note) {
this.updateTitle($tab, t('tab_row.new_tab'));
return;
}
}
const title = await noteContext.getNavigationTitle();
this.updateTitle($tab, title);
@ -696,6 +700,17 @@ export default class TabRowWidget extends BasicWidget {
$tab.addClass(note.getCssClass());
$tab.addClass(utils.getNoteTypeClass(note.type));
$tab.addClass(utils.getMimeTypeClass(note.mime));
if (this.showNoteIcons) {
noteIcon = note.getIcon();
}
if (noteIcon) {
$tab.find('.note-tab-icon')
.removeClass()
.addClass("note-tab-icon")
.addClass(noteIcon);
}
}
async entitiesReloadedEvent({loadResults}) {

View File

@ -54,7 +54,7 @@ const TPL = `
cursor: text !important;
}
.note-detail-editable-text *:not(figure,.include-note):first-child {
.note-detail-editable-text *:not(figure, .include-note, hr):first-child {
margin-top: 0 !important;
}

View File

@ -509,7 +509,11 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th
.tooltip {
font-size: var(--main-font-size) !important;
z-index: calc(var(--ck-z-panel) - 1) !important;
/*
TODO: Investigate the purpose of this
z-index: calc(var(--ck-z-panel) - 1) !important;
*/
z-index: 3000;
}
.tooltip-trigger {

View File

@ -40,7 +40,7 @@
--launcher-pane-size: 58px;
--launcher-pane-horizontal-size: 54px;
--launcher-pane-horizontal-icon-size: 20px;
--launcher-pane-horizontal-icon-size: 20px;
--launcher-pane-button-margin: 6px;
--launcher-pane-button-gap: 3px;
@ -55,6 +55,15 @@
--menu-item-icon-vert-offset: 0;
--more-accented-background-color: var(--card-background-hover-color);
--timeline-left-gap: 20px;
--timeline-right-gap: 20px;
--timeline-bullet-size: 10px;
--timeline-bullet-vertical-pos: .75em;
--timeline-connector-size: 4px;
/* Theme capabilities */
--tab-note-icons: true;
}
/*
@ -135,11 +144,14 @@
--launcher-pane-background-color: #e8e8e8;
--launcher-pane-horizontal-background-color: #fafafa;
--launcher-pane-horizontal-border-color: rgba(0, 0, 0, 0.1);
--launcher-pane-text-color: #000000bd;
--launcher-pane-button-hover-color: black;
--launcher-pane-button-hover-background: white;
--launcher-pane-button-hover-shadow: 4px 4px 4px rgba(0, 0, 0, .075);
--sync-status-error-pulse-color: #ff5528;
--root-background: var(--left-pane-background-color);
--gutter-color: transparent;
@ -169,10 +181,9 @@
--scrollbar-border-color: #ddd;
--scrollbar-background-color: #ddd;
--tooltip-background-color: #f8f8f8;
--link-color: blue;
--mermaid-theme: default;
--mermaid-theme: default;
--code-block-box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.1), 0px 0px 2px rgba(0, 0, 0, 0.2);
@ -182,6 +193,21 @@
--card-border-color: #eaeaea;
--card-shadow-color: rgba(0, 0, 0, 0.1);
--card-box-shadow: 0 0 12px var(--card-shadow-color);
--calendar-color: var(--menu-text-color);
--calendar-weekday-labels-color: var(--muted-text-color);
--calendar-day-hover-color: var(--hover-item-text-color);
--calendar-day-hover-background: var(--active-item-background-color);
--calendar-day-highlight-background: #80808024;
--timeline-bullet-color: #a5a5a5;
--timeline-bullet-hover-color: black;
--timeline-connector-color: #f1f1f1;
--timeline-connector-active-color: #ddd;
--tooltip-background-color: rgba(255, 255, 255, 0.85);
--tooltip-foreground-color: #000000ba;
--tooltip-shadow-color: rgba(0, 0, 0, .15);
}
/*
@ -195,8 +221,8 @@
--main-background-color: #333;
--main-text-color: #ccc;
--main-border-color: #454545;
--subtle-border-color: rgba(0, 0, 0, 0.3);
--dropdown-border-color: #555;
--subtle-border-color: #313131;
--dropdown-border-color: #292929;
--dropdown-shadow-opacity: .6;
--dropdown-item-icon-destructive-color: #de6e5b;
--disabled-tooltip-icon-color: #7fd2ef;
@ -268,11 +294,14 @@
--launcher-pane-background-color: #1a1a1a;
--launcher-pane-horizontal-background-color: #282828;
--launcher-pane-horizontal-border-color: rgb(22, 22, 22);
--launcher-pane-text-color: #909090;
--launcher-pane-button-hover-color: #ffffff;
--launcher-pane-button-hover-background: #ffffff1c;
--launcher-pane-button-hover-shadow: 4px 4px 4px rgba(0, 0, 0, .2);
--sync-status-error-pulse-color: #f47871;
--root-background: var(--left-pane-background-color);
--gutter-color: transparent;
@ -296,7 +325,6 @@
--scrollbar-border-color: #666;
--scrollbar-background-color: #333;
--tooltip-background-color: #333;
--link-color: lightskyblue;
--mermaid-theme: dark;
@ -308,6 +336,21 @@
--card-background-press-color: #464646;
--card-border-color: #222222;
--card-box-shadow: 0 0 12px rgba(0, 0, 0, 0.15);
--calendar-color: var(--menu-text-color);
--calendar-weekday-labels-color: var(--muted-text-color);
--calendar-day-hover-color: var(--hover-item-text-color);
--calendar-day-hover-background: var(--active-item-background-color);
--calendar-day-highlight-background: #8080805a;
--timeline-bullet-color: gray;
--timeline-bullet-hover-color: white;
--timeline-connector-color: #464646;
--timeline-connector-active-color: #545454;
--tooltip-background-color: rgba(67, 67, 67, 0.86);
--tooltip-foreground-color: #ffffffeb;
--tooltip-shadow-color: rgba(0, 0, 0, 0.4);
}
body ::-webkit-calendar-picker-indicator {
@ -377,6 +420,18 @@ body.layout-horizontal {
border-right: 2px solid var(--left-pane-collapsed-border-color);
}
/*
* Global menu
*/
.global-menu div.zoom-buttons a {
border: unset;
}
.global-menu div.zoom-buttons a.bx {
color: var(--menu-text-color) !important;
}
/*
* Launcher pane
*/
@ -392,7 +447,7 @@ body.layout-horizontal > .horizontal {
#launcher-pane.horizontal {
height: var(--launcher-pane-size) !important;
border-bottom: 1px solid var(--subtle-border-color);
border-bottom: 1px solid var(--launcher-pane-horizontal-border-color);
}
#launcher-pane .launcher-button,
@ -431,6 +486,33 @@ body.layout-horizontal > .horizontal {
box-shadow 100ms ease-in;
}
#launcher-pane div.launcher-button {
display: flex;
justify-content: center;
align-items: center;
}
#launcher-pane .sync-status .sync-status-icon {
top: 3px;
}
#launcher-pane .sync-status-icon:not(.sync-status-in-progress):hover {
background: unset;
}
@keyframes sync-status-pulse {
from {
color: currentColor
} to {
color: var(--pulse-color);
}
}
#launcher-pane .sync-status-disconnected-with-changes {
--pulse-color: var(--sync-status-error-pulse-color);
animation: sync-status-pulse 1s ease-in-out alternate-reverse infinite;
}
#launcher-pane.horizontal .launcher-button {
font-size: var(--launcher-pane-horizontal-icon-size);
}
@ -439,13 +521,25 @@ body.layout-horizontal > .horizontal {
--hover-item-background-color: transparent;
}
/*
* Left pane
*/
.tooltip .tooltip-arrow {
display: none;
}
/* Search Box */
.tooltip .tooltip-inner {
padding: 6px 10px !important;
box-shadow: -1px -1px 2px var(--tooltip-shadow-color),
2px 2px 8px var(--tooltip-shadow-color) !important;
border: unset !important;
border-radius: 6px;
backdrop-filter: blur(5px);
color: var(--tooltip-foreground-color) !important;
}
#left-pane .quick-search {
/*
* Search Box
*/
div.quick-search {
--padding-top: 8px;
--padding-left: 8px;
--padding-right: 8px;
@ -459,7 +553,7 @@ body.layout-horizontal > .horizontal {
padding: var(--padding-top) var(--padding-right) var(--padding-bottom) var(--padding-left);
}
#left-pane .quick-search::before {
div.quick-search::before {
/* The background rectangle of the search box */
position: absolute;
content: "";
@ -474,34 +568,34 @@ body.layout-horizontal > .horizontal {
transition: background-color 200ms ease-in;
}
#left-pane .quick-search:hover:before {
div.quick-search:hover:before {
/* Hovered search box background rectangle */
background: var(--quick-search-hover-background);
transition: background-color 75ms ease-out;
}
#left-pane .quick-search:focus-within:before {
div.quick-search:focus-within:before {
/* Focused search box background rectangle */
border-color: var(--quick-search-focus-border);
background: var(--quick-search-focus-background);
transition: background-color 100ms ease-out;
}
#left-pane .quick-search input {
padding-left: 15px;
box-shadow: unset;
background: transparent;
div.quick-search input {
padding-left: 15px !important;
box-shadow: unset !important;
background: transparent !important;
}
#left-pane .quick-search input::placeholder {
div.quick-search input::placeholder {
color: var(--quick-search-color);
}
#left-pane .quick-search:focus-within input {
div.quick-search:focus-within input {
color: var(--quick-search-focus-color) !important;
}
#left-pane .quick-search .search-button {
div.quick-search .search-button {
display: flex;
align-items: center;
justify-content: center;
@ -514,27 +608,30 @@ body.layout-horizontal > .horizontal {
transition: background-color 200ms ease-in !important;
}
#left-pane .quick-search .search-button:active {
div.quick-search .search-button:active {
transform: scale(.85);
}
#left-pane .quick-search:focus-within:has(input:not(:placeholder-shown)) .search-button {
div.quick-search:focus-within:has(input:not(:placeholder-shown)) .search-button {
/* Matches when the input has a value and the focus is inside the search box */
background: var(--left-pane-item-action-button-background) !important;
color: var(--left-pane-item-action-button-color) !important;
transition: background-color 500ms ease-out !important;
}
html body #left-pane .quick-search:focus-within .search-button:hover,
#left-pane .quick-search .search-button.show {
html body .quick-search:focus-within .search-button:hover,
div.quick-search .search-button.show {
/* Hover state */
background: var(--left-pane-item-action-button-hover-background) !important;
color: var(--left-pane-item-action-button-color) !important;
transition: background-color 100ms ease-out !important;
}
/* Tree */
/*
* Left pane
*/
/* Tree */
#left-pane .tree-actions {
/* TODO: relocate instead of hiding */
@ -706,7 +803,7 @@ body.layout-horizontal .tab-row-container:after {
left: 0;
right: 0;
height: 1px;
background: var(--subtle-border-color);
border-bottom: 1px solid var(--launcher-pane-horizontal-border-color);
}
body.layout-vertical.electron.platform-darwin .tab-row-container {
@ -728,6 +825,17 @@ body.layout-horizontal .tab-row-widget-container {
overflow: hidden;
}
#root-widget.horizontal-layout .tab-row-widget .note-tab .note-tab-wrapper {
border: 1px solid transparent;
border-bottom-color: transparent;
box-shadow: unset;
}
#root-widget.horizontal-layout .tab-row-widget .note-tab[active] .note-tab-wrapper {
border: 1px solid var(--launcher-pane-horizontal-border-color);
border-bottom-color: transparent;
}
.tab-row-widget .note-tab .note-tab-wrapper {
height: var(--tab-height) !important;
transition: background 75ms ease-in,
@ -743,6 +851,20 @@ body.layout-horizontal .tab-row-widget .note-tab .note-tab-wrapper {
border-bottom-right-radius: 0;
}
.note-tab .note-tab-wrapper {
--tab-background-color: initial !important;
}
.note-tab .note-tab-wrapper::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background-color: var(--workspace-tab-background-color);
}
.tab-row-widget .note-tab:nth-child(1) {
transform: translate3d(var(--tab-first-item-horiz-offset), 0, 0);
}
@ -850,11 +972,14 @@ body.layout-horizontal .tab-row-widget .note-tab .note-tab-wrapper {
*/
#center-pane {
border-radius: var(--center-pane-border-radius) 0 0 0;
padding-top: 2px;
background: var(--main-background-color);
}
.vertical-layout #center-pane {
border-radius: var(--center-pane-border-radius) 0 0 0;
}
/*
* Ribbon & note header
*/
@ -979,6 +1104,216 @@ html body .dropdown-item.disabled {
color: var(--menu-item-arrow-color) !important;
}
/*
* Calendar
*/
.calendar-dropdown-widget {
padding: 12px;
color: var(--calendar-color);
}
.calendar-dropdown-widget .calendar-header {
padding: 8px 0 20px 0;
}
.calendar-dropdown-widget .calendar-header input[type="number"] {
appearance: textfield !important;
}
.calendar-dropdown-widget .calendar-header input[type="number"]::-webkit-outer-spin-button,
.calendar-dropdown-widget .calendar-header input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.calendar-dropdown-widget .calendar-header input,
.calendar-dropdown-widget .calendar-header select {
/* TODO: Provide styling for background and states */
border: unset;
text-align: center;
font-size: 1.4em;
font-weight: 300;
}
.calendar-dropdown-widget .calendar-week span {
font-size: .85em;
font-weight: 500;
color: var(--calendar-weekday-labels-color);
}
.calendar-dropdown-widget .calendar-body {
font-size: .9em;
}
.calendar-dropdown-widget .calendar-body a {
background: transparent;
color: var(--calendar-color);
}
.calendar-dropdown-widget .calendar-body a.calendar-date-exists {
position: relative;
text-decoration: none !important;
}
.calendar-dropdown-widget .calendar-body a.calendar-date-exists:not(:hover)::before {
--vertical-margin: 13%;
--horiz-margin: 18%;
content: "";
position: absolute;
top: var(--vertical-margin);
right: var(--horiz-margin);
bottom: var(--vertical-margin);
left: var(--horiz-margin);
border-radius: 6px;
background: var(--calendar-day-highlight-background);
z-index: -1;
}
body .calendar-dropdown-widget .calendar-body a:hover {
border-radius: 6px;
background: var(--calendar-day-hover-background);
color: var(--calendar-day-hover-color) !important;
}
/*
* Note tooltip
*/
.tooltip .tooltip-inner:has(.note-tooltip-content) {
border-radius: 8px;
}
.note-tooltip-content {
padding: 8px;
}
.note-tooltip-content .note-title-with-path {
display: flex;
flex-direction: column-reverse;
border-bottom: 2px solid currentColor;
padding-bottom: 6px;
}
.note-tooltip-content .note-title-with-path .path-bracket {
/* Hide the leading and trailing bracket from the path */
display: none;
}
.note-tooltip-content .note-title-with-path .path-delimiter {
/* Hide the path delimiters (slashes) */
display: none;
}
.note-tooltip-content .note-title-with-path .path-delimiter + span::before {
/* Replace the path delimiters with arrows */
display: inline-block;
content: "\ebe6";
padding: 0 4px;
line-height: 1;
vertical-align: bottom;
font-family: boxicons;
opacity: .75;
}
.note-tooltip-content .note-path {
display: block;
color: var(--muted-text-color);
font-size: .75em;
}
.note-tooltip-content .note-tooltip-attributes {
margin-top: -4px;
font-size: .75em;
}
.note-tooltip-content .rendered-content {
padding-top: 12px;
}
/*
* Recent changes list
*/
.recent-changes-content small {
color: var(--muted-text-color);
}
.recent-changes-content > div {
padding-left: var(--timeline-left-gap);
}
/* Date headings */
.recent-changes-content > div > b {
display: block;
padding: 10px 0;
font-size: 1.25em;
font-weight: 300;
}
.recent-changes-content ul {
list-style: none;
margin: 0;
padding: 0;
}
/* Timeline items */
.recent-changes-content ul li,
.recent-changes-content > div > b {
position: relative;
margin: 0;
padding-left: var(--timeline-right-gap);
}
/* Timeline connector */
.recent-changes-content ul li::before,
.recent-changes-content > div > b::before {
position: absolute;
content: "";
top: var(--connector-top, 0);
left: calc((var(--timeline-bullet-size) - var(--timeline-connector-size)) / 2);
bottom: var(--connector-bottom, 0);
width: var(--timeline-connector-size);
border-radius: var(--connector-radius, 0) var(--connector-radius, 0) 0 0;
background: var(--timeline-connector-color);
transition: background-color 400ms ease-in-out;
}
.recent-changes-content > div:hover {
--timeline-connector-color: var(--timeline-connector-active-color);
}
/* The first item of the timeline */
.recent-changes-content > div:first-child > *:first-child {
--connector-top: 50%;
--connector-radius: calc(var(--timeline-connector-size) / 2);
}
/* The last item of the timeline */
.recent-changes-content > div:last-child li:last-child {
--connector-bottom: 50%;
}
/* Timeline bullet */
.recent-changes-content ul li::after {
position: absolute;
content: "";
top: var(--timeline-bullet-vertical-pos);
left: 0;
width: var(--timeline-bullet-size);
height: var(--timeline-bullet-size);
border-radius: 50%;
background: var(--timeline-bullet-color);
transform: translateY(-50%);
}
/* Hovered timeline bullet */
.recent-changes-content ul li:hover::after {
background: var(--timeline-bullet-hover-color);
}
/*
* TEXT NOTES
*/
@ -996,7 +1331,7 @@ html body .dropdown-item.disabled {
overflow: unset;
}
html .note-detail-editable-text :not(figure, .include-note):first-child {
html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
/* Create some space for the top-side shadow */
margin-top: 1px !important;
}
@ -1029,6 +1364,52 @@ html .note-detail-editable-text :not(figure, .include-note):first-child {
cursor: default;
}
.ck-content blockquote {
background: var(--card-background-color);
border: 1px solid var(--card-border-color) !important;
box-shadow: var(--card-box-shadow);
border-radius: 10px;
padding: 1em 2.5em;
position: relative;
font-style: unset !important;
}
.ck-content blockquote p:last-of-type {
margin-bottom: 0 !important;
}
.ck-content blockquote:before,
.ck-content blockquote:after {
position: absolute;
top: 0;
font-size: 48px;
opacity: 0.1;
}
.ck-content blockquote:before {
content: "“";
left: 0.2em;
}
.ck-content blockquote:after {
content: "”";
right: 0.35em;
}
.ck-content hr {
margin: 5px 0;
height: 1px;
background-color: var(--main-border-color);
opacity: 1;
}
.ck-content p code {
border: 1px solid var(--card-border-color);
box-shadow: var(--card-box-shadow);
border-radius: 6px;
background-color: var(--card-background-color);
}
.note-detail-printable:not(.word-wrap) pre code {
white-space: pre;
margin-right: 1em;
@ -1102,12 +1483,14 @@ html .note-detail-editable-text :not(figure, .include-note):first-child {
* Note list
*/
.note-list .note-book-card {
--note-list-horizontal-padding: 22px;
--note-list-vertical-padding: 15px;
background-color: var(--card-background-color);
border: 1px solid var(--card-border-color) !important;
box-shadow: 2px 3px 4px var(--card-shadow-color);
border-radius: 12px;
user-select: none;
padding: 15px 22px;
padding: 0;
margin: 5px 10px 5px 0;
}
@ -1128,9 +1511,10 @@ html .note-detail-editable-text :not(figure, .include-note):first-child {
}
.note-list-wrapper .note-book-card .note-book-header {
font-size: 1.1em;
font-size: 1em;
font-weight: bold;
margin-bottom: 0.25em;
padding: 0.5em 1rem;
border-bottom-color: var(--card-border-color);
}
.note-list-wrapper .note-book-card .note-book-header .note-icon {
@ -1144,28 +1528,77 @@ html .note-detail-editable-text :not(figure, .include-note):first-child {
vertical-align: middle;
}
.note-list-wrapper .note-book-card .note-book-header:last-child {
border-bottom: 0;
.note-list-wrapper .note-book-card .note-book-header .rendered-note-attributes {
font-size: 0.7em;
font-weight: normal;
margin-bottom: 0;
padding-bottom: 0;
}
.note-list-wrapper .note-book-card .note-book-header .note-book-content {
.note-list-wrapper .note-book-card .note-book-header:last-child {
border-bottom: 0;
}
.note-list-wrapper .note-book-card .note-book-content {
padding: 0 !important;
font-size: 0.8rem;
}
.note-list-wrapper .note-book-card .note-book-header .note-book-content h1,
.note-list-wrapper .note-book-card .note-book-header .note-book-content h2,
.note-list-wrapper .note-book-card .note-book-header .note-book-content h3,
.note-list-wrapper .note-book-card .note-book-header .note-book-content h4,
.note-list-wrapper .note-book-card .note-book-header .note-book-content h5,
.note-list-wrapper .note-book-card .note-book-header .note-book-content h6 {
.note-list-wrapper .note-book-card .note-book-content .rendered-content {
padding: 1rem;
}
.note-list-wrapper .note-book-card .note-book-content .rendered-content.text-with-ellipsis {
padding: 1rem !important;
}
.note-list-wrapper .note-book-card .note-book-content h1,
.note-list-wrapper .note-book-card .note-book-content h2,
.note-list-wrapper .note-book-card .note-book-content h3,
.note-list-wrapper .note-book-card .note-book-content h4,
.note-list-wrapper .note-book-card .note-book-content h5,
.note-list-wrapper .note-book-card .note-book-content h6 {
font-size: 1rem;
color: var(--active-item-text-color);
}
.note-list-wrapper .note-book-card .bx {
color: var(--left-pane-icon-color);
font-weight: bold;
.note-list-wrapper .note-book-card .note-book-content p:last-child {
margin-bottom: 0;
}
.note-list-wrapper .note-book-card .note-book-content.type-canvas .rendered-content,
.note-list-wrapper .note-book-card .note-book-content.type-mindMap .rendered-content,
.note-list-wrapper .note-book-card .note-book-content.type-code .rendered-content {
padding: 0;
}
.note-list-wrapper .note-book-card .note-book-content.type-code pre {
height: 100%;
padding: 1em;
}
.note-list-wrapper .note-book-card .bx {
color: var(--left-pane-icon-color) !important;
}
.note-list.grid-view .note-book-card:hover {
background: var(--card-background-color) !important;
filter: contrast(105%);
}
.note-list.grid-view .note-book-card img {
object-fit: cover !important;
width: 100%;
}
.note-list.grid-view .ck-content {
line-height: 1.3;
}
.note-list.grid-view .ck-content p {
margin-bottom: 0.5em;
}
.note-list.grid-view .ck-content figure.image {
width: 25%;
}

View File

@ -138,7 +138,7 @@ span.fancytree-node.protected > span.fancytree-custom-icon {
span.fancytree-node.multiple-parents.shared .fancytree-title::after {
font-family: 'boxicons' !important;
font-size: smaller;
content: " \eb3d \ec03";
content: " \eb3d\ec03";
}
span.fancytree-node.multiple-parents .fancytree-title::after {

View File

@ -93,7 +93,7 @@
"export_note_title": "导出笔记",
"close": "关闭",
"export_type_subtree": "此笔记及其所有子笔记",
"format_html": "HTML ZIP 归档 - 建议使用此选项,因为它保留了所有格式。",
"format_html_zip": "HTML ZIP 归档 - 建议使用此选项,因为它保留了所有格式。",
"format_markdown": "Markdown - 保留大部分格式。",
"format_opml": "OPML - 大纲交换格式,仅限文本。不包括格式、图像和文件。",
"opml_version_1": "OPML v1.0 - 仅限纯文本",

View File

@ -88,7 +88,7 @@
"export_note_title": "Notiz exportieren",
"close": "Schließen",
"export_type_subtree": "diese Notiz und alle ihre Unternotizen",
"format_html": "HTML im ZIP-Archiv dies wird empfohlen, da dadurch die gesamte Formatierung erhalten bleibt.",
"format_html_zip": "HTML im ZIP-Archiv dies wird empfohlen, da dadurch die gesamte Formatierung erhalten bleibt.",
"format_markdown": "Markdown dadurch bleiben die meisten Formatierungen erhalten.",
"format_opml": "OPML Outliner-Austauschformat nur für Text. Formatierungen, Bilder und Dateien sind nicht enthalten.",
"opml_version_1": "OPML v1.0 nur Klartext",

View File

@ -93,7 +93,8 @@
"export_note_title": "Export note",
"close": "Close",
"export_type_subtree": "this note and all of its descendants",
"format_html": "HTML in ZIP archive - this is recommended since this preserves all the formatting.",
"format_html": "HTML - recomandat deoarece păstrează toată formatarea",
"format_html_zip": "HTML in ZIP archive - this is recommended since this preserves all the formatting.",
"format_markdown": "Markdown - this preserves most of the formatting.",
"format_opml": "OPML - outliner interchange format for text only. Formatting, images and files are not included.",
"opml_version_1": "OPML v1.0 - plain text only",

View File

@ -93,7 +93,7 @@
"export_note_title": "Exportar nota",
"close": "Cerrar",
"export_type_subtree": "esta nota y todos sus descendientes",
"format_html": "HTML en un archivo ZIP: se recomienda ya que conserva todo el formato.",
"format_html_zip": "HTML en un archivo ZIP: se recomienda ya que conserva todo el formato.",
"format_markdown": "Markdown: esto conserva la mayor parte del formato.",
"format_opml": "OPML: formato de intercambio de esquemas solo para texto. El formato, las imágenes y los archivos no están incluidos.",
"opml_version_1": "OPML v1.0: solo texto sin formato",

View File

@ -89,7 +89,7 @@
"export_note_title": "Exporter la note",
"close": "Fermer",
"export_type_subtree": "cette note et tous ses descendants",
"format_html": "HTML dans l'archive ZIP - recommandé car cela préserve tout le formatage.",
"format_html_zip": "HTML dans l'archive ZIP - recommandé car cela préserve tout le formatage.",
"format_markdown": "Markdown - préserve la majeure partie du formatage.",
"format_opml": "OPML - format d'échange pour les outlineurs, uniquement pour le texte. Les mises en forme, images et fichiers ne sont pas inclus.",
"opml_version_1": "OPML v1.0 - texte brut uniquement",

View File

@ -509,7 +509,7 @@
"export_status": "Starea exportului",
"export_type_single": "doar această notiță fără descendenții ei",
"export_type_subtree": "această notiță și toți descendenții ei",
"format_html": "HTML în arhivă ZIP - recomandat deoarece păstrează toată formatarea",
"format_html_zip": "HTML în arhivă ZIP - recomandat deoarece păstrează toată formatarea",
"format_markdown": "Markdown - păstrează majoritatea formatării",
"format_opml": "OPML - format de interschimbare pentru editoare cu structură ierarhică (outline). Formatarea, imaginile și fișierele nu vor fi incluse.",
"opml_version_1": "OPML v1.0 - text simplu",

View File

@ -93,7 +93,7 @@
"export_note_title": "匯出筆記",
"close": "關閉",
"export_type_subtree": "此筆記及其所有子筆記",
"format_html": "HTML ZIP 歸檔 - 建議使用此選項,因為它保留了所有格式。",
"format_html_zip": "HTML ZIP 歸檔 - 建議使用此選項,因為它保留了所有格式。",
"format_markdown": "Markdown - 保留大部分格式。",
"format_opml": "OPML - 大綱交換格式,僅限文字。不包括格式、圖片和文件。",
"opml_version_1": "OPML v1.0 - 僅限純文字",

View File

@ -52,13 +52,15 @@ function sanitize(dirtyHtml: string) {
return sanitizeHtml(dirtyHtml, {
allowedTags,
allowedAttributes: {
'*': [ 'class', 'style', 'title', 'src', 'href', 'hash', 'disabled', 'align', 'alt', 'center', 'data-*' ]
"*": [ 'class', 'style', 'title', 'src', 'href', 'hash', 'disabled', 'align', 'alt', 'center', 'data-*' ],
"input": [ "type", "checked" ]
},
// Be consistent with `allowedSchemes` in `src\public\app\services\link.js`
allowedSchemes: [
'http', 'https', 'ftp', 'ftps', 'mailto', 'data', 'evernote', 'file', 'facetime', 'irc', 'gemini', 'git',
'http', 'https', 'ftp', 'ftps', 'mailto', 'data', 'evernote', 'file', 'facetime', 'gemini', 'git',
'gopher', 'imap', 'irc', 'irc6', 'jabber', 'jar', 'lastfm', 'ldap', 'ldaps', 'magnet', 'message',
'mumble', 'nfs', 'onenote', 'pop', 'rmi', 's3', 'sftp', 'skype', 'sms', 'spotify', 'steam', 'svn', 'udp',
'view-source', 'vnc', 'ws', 'wss', 'xmpp', 'jdbc', 'slack'
'view-source', 'vlc', 'vnc', 'ws', 'wss', 'xmpp', 'jdbc', 'slack', 'tel', 'smb', 'zotero'
],
nonTextTags: [
'head'