Improved right-to-left support (#7227)

This commit is contained in:
Elian Doran
2025-10-09 21:22:33 +03:00
committed by GitHub
96 changed files with 635 additions and 483 deletions

View File

@@ -10,7 +10,6 @@ import { t } from "./services/i18n.js";
import options from "./services/options.js"; import options from "./services/options.js";
import type ElectronRemote from "@electron/remote"; import type ElectronRemote from "@electron/remote";
import type Electron from "electron"; import type Electron from "electron";
import "bootstrap/dist/css/bootstrap.min.css";
import "boxicons/css/boxicons.min.css"; import "boxicons/css/boxicons.min.css";
import "autocomplete.js/index_jquery.js"; import "autocomplete.js/index_jquery.js";

View File

@@ -40,8 +40,8 @@ kbd {
border: none; border: none;
cursor: pointer; cursor: pointer;
font-size: 1.25em; font-size: 1.25em;
padding-left: 0.5em; padding-inline-start: 0.5em;
padding-right: 0.5em; padding-inline-end: 0.5em;
color: var(--main-text-color); color: var(--main-text-color);
} }
.quick-search { .quick-search {
@@ -59,7 +59,7 @@ const FANCYTREE_CSS = `
margin-top: 0px; margin-top: 0px;
overflow-y: auto; overflow-y: auto;
contain: content; contain: content;
padding-left: 10px; padding-inline-start: 10px;
} }
.fancytree-custom-icon { .fancytree-custom-icon {
@@ -68,7 +68,7 @@ const FANCYTREE_CSS = `
.fancytree-title { .fancytree-title {
font-size: 1.5em; font-size: 1.5em;
margin-left: 0.6em !important; margin-inline-start: 0.6em !important;
} }
.fancytree-node { .fancytree-node {
@@ -81,7 +81,7 @@ const FANCYTREE_CSS = `
span.fancytree-expander { span.fancytree-expander {
width: 24px !important; width: 24px !important;
margin-right: 5px; margin-inline-end: 5px;
} }
.fancytree-loading span.fancytree-expander { .fancytree-loading span.fancytree-expander {
@@ -101,7 +101,7 @@ span.fancytree-expander {
.tree-wrapper .scroll-to-active-note-button, .tree-wrapper .scroll-to-active-note-button,
.tree-wrapper .tree-settings-button { .tree-wrapper .tree-settings-button {
position: fixed; position: fixed;
margin-right: 16px; margin-inline-end: 16px;
display: none; display: none;
} }
@@ -126,8 +126,8 @@ export default class MobileLayout {
.class("d-md-flex d-lg-flex d-xl-flex col-12 col-sm-5 col-md-4 col-lg-3 col-xl-3") .class("d-md-flex d-lg-flex d-xl-flex col-12 col-sm-5 col-md-4 col-lg-3 col-xl-3")
.id("mobile-sidebar-wrapper") .id("mobile-sidebar-wrapper")
.css("max-height", "100%") .css("max-height", "100%")
.css("padding-left", "0") .css("padding-inline-start", "0")
.css("padding-right", "0") .css("padding-inline-end", "0")
.css("contain", "content") .css("contain", "content")
.child(new FlexContainer("column").filling().id("mobile-sidebar-wrapper").child(new QuickSearchWidget()).child(new NoteTreeWidget().cssBlock(FANCYTREE_CSS))) .child(new FlexContainer("column").filling().id("mobile-sidebar-wrapper").child(new QuickSearchWidget()).child(new NoteTreeWidget().cssBlock(FANCYTREE_CSS)))
) )

View File

@@ -1,5 +1,3 @@
import "bootstrap/dist/css/bootstrap.min.css";
// @ts-ignore - module = undefined // @ts-ignore - module = undefined
// Required for correct loading of scripts in Electron // Required for correct loading of scripts in Electron
if (typeof module === 'object') {window.module = module; module = undefined;} if (typeof module === 'object') {window.module = module; module = undefined;}

View File

@@ -150,8 +150,8 @@ class ContextMenu {
this.$widget this.$widget
.css({ .css({
display: "block", display: "block",
top: top, top,
left: left left
}) })
.addClass("show"); .addClass("show");
} }
@@ -187,7 +187,7 @@ class ContextMenu {
} }
// Create a new group to avoid column breaks before and after the seaparator / header. // Create a new group to avoid column breaks before and after the seaparator / header.
// This is a workaround for Firefox not supporting break-before / break-after: avoid // This is a workaround for Firefox not supporting break-before / break-after: avoid
// for columns. // for columns.
if (shouldStartNewGroup) { if (shouldStartNewGroup) {
$group = $("<div class='dropdown-no-break'>"); $group = $("<div class='dropdown-no-break'>");
@@ -313,7 +313,7 @@ class ContextMenu {
} }
$group.append($item); $group.append($item);
// After adding a menu item, if the previous item was a separator or header, // After adding a menu item, if the previous item was a separator or header,
// reset the group so that the next item will be appended directly to the parent. // reset the group so that the next item will be appended directly to the parent.
if (shouldResetGroup) { if (shouldResetGroup) {

View File

@@ -1,7 +1,6 @@
import appContext from "./components/app_context.js"; import appContext from "./components/app_context.js";
import noteAutocompleteService from "./services/note_autocomplete.js"; import noteAutocompleteService from "./services/note_autocomplete.js";
import glob from "./services/glob.js"; import glob from "./services/glob.js";
import "bootstrap/dist/css/bootstrap.min.css";
import "boxicons/css/boxicons.min.css"; import "boxicons/css/boxicons.min.css";
import "autocomplete.js/index_jquery.js"; import "autocomplete.js/index_jquery.js";

View File

@@ -1,5 +1,15 @@
import $ from "jquery"; import $ from "jquery";
async function loadBootstrap() {
if (document.body.dir === "rtl") {
await import("bootstrap/dist/css/bootstrap.rtl.min.css");
} else {
await import("bootstrap/dist/css/bootstrap.min.css");
}
}
(window as any).$ = $; (window as any).$ = $;
(window as any).jQuery = $; (window as any).jQuery = $;
await loadBootstrap();
$("body").show(); $("body").show();

View File

@@ -6,7 +6,7 @@ import { describe, expect, it } from "vitest";
describe("i18n", () => { describe("i18n", () => {
it("translations are valid JSON", () => { it("translations are valid JSON", () => {
for (const locale of LOCALES) { for (const locale of LOCALES) {
if (locale.contentOnly) { if (locale.contentOnly || locale.id === "en_rtl") {
continue; continue;
} }

View File

@@ -869,6 +869,18 @@ export function getErrorMessage(e: unknown) {
} }
} }
/**
* Handles left or right placement of e.g. tooltips in case of right-to-left languages. If the current language is a RTL one, then left and right are swapped. Other directions are unaffected.
* @param placement a string optionally containing a "left" or "right" value.
* @returns a left/right value swapped if needed, or the same as input otherwise.
*/
export function handleRightToLeftPlacement<T extends string>(placement: T) {
if (!glob.isRtl) return placement;
if (placement === "left") return "right";
if (placement === "right") return "left";
return placement;
}
export default { export default {
reloadFrontendApp, reloadFrontendApp,
restartDesktopApp, restartDesktopApp,

View File

@@ -1,4 +1,3 @@
import "bootstrap/dist/css/bootstrap.min.css";
import "./stylesheets/auth.css"; import "./stylesheets/auth.css";
// @TriliumNextTODO: is this even needed anymore? // @TriliumNextTODO: is this even needed anymore?

View File

@@ -1,7 +1,6 @@
import "jquery"; import "jquery";
import utils from "./services/utils.js"; import utils from "./services/utils.js";
import ko from "knockout"; import ko from "knockout";
import "bootstrap/dist/css/bootstrap.min.css";
// TriliumNextTODO: properly make use of below types // TriliumNextTODO: properly make use of below types
// type SetupModelSetupType = "new-document" | "sync-from-desktop" | "sync-from-server" | ""; // type SetupModelSetupType = "new-document" | "sync-from-desktop" | "sync-from-server" | "";

View File

@@ -60,7 +60,7 @@
appearance: none; appearance: none;
text-align: center; text-align: center;
border: 0; border: 0;
border-left: unset; border-inline-start: unset;
background-color: var(--menu-background-color); background-color: var(--menu-background-color);
font-weight: bold; font-weight: bold;
outline: 0; outline: 0;
@@ -102,7 +102,7 @@
content: ''; content: '';
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; inset-inline-end: 0;
bottom: 0; bottom: 0;
width: 1px; width: 1px;
background-color: var(--main-border-color); background-color: var(--main-border-color);

View File

@@ -299,7 +299,7 @@
content: ''; content: '';
position: absolute; position: absolute;
top: 0; top: 0;
left: -100%; inset-inline-start: -100%;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: linear-gradient(90deg, transparent, var(--hover-item-background-color, rgba(0, 0, 0, 0.03)), transparent); background: linear-gradient(90deg, transparent, var(--hover-item-background-color, rgba(0, 0, 0, 0.03)), transparent);
@@ -406,10 +406,10 @@
@keyframes shimmer { @keyframes shimmer {
0% { 0% {
left: -100%; inset-inline-start: -100%;
} }
100% { 100% {
left: 100%; inset-inline-start: 100%;
} }
} }

View File

@@ -218,7 +218,7 @@ span[style] {
--box-vert-offset: calc((1lh - var(--box-size)) / 2); --box-vert-offset: calc((1lh - var(--box-size)) / 2);
display: inline-block; display: inline-block;
padding-left: calc(var(--box-size) + var(--box-text-gap)); padding-inline-start: calc(var(--box-size) + var(--box-text-gap));
/* Source: https://pictogrammers.com/library/mdi/icon/checkbox-blank-outline/ */ /* Source: https://pictogrammers.com/library/mdi/icon/checkbox-blank-outline/ */
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3e%3cpath d='M19%2c3H5C3.89%2c3 3%2c3.89 3%2c5V19A2%2c2 0 0%2c0 5%2c21H19A2%2c2 0 0%2c0 21%2c19V5C21%2c3.89 20.1%2c3 19%2c3M19%2c5V19H5V5H19Z' /%3e%3c/svg%3e"); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3e%3cpath d='M19%2c3H5C3.89%2c3 3%2c3.89 3%2c5V19A2%2c2 0 0%2c0 5%2c21H19A2%2c2 0 0%2c0 21%2c19V5C21%2c3.89 20.1%2c3 19%2c3M19%2c5V19H5V5H19Z' /%3e%3c/svg%3e");
background-position: 0 var(--box-vert-offset); background-position: 0 var(--box-vert-offset);
@@ -281,7 +281,7 @@ span[style] {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }

View File

@@ -62,7 +62,7 @@
.note-detail-relation-map .endpoint { .note-detail-relation-map .endpoint {
position: absolute; position: absolute;
bottom: 37%; bottom: 37%;
right: 5px; inset-inline-end: 5px;
width: 1em; width: 1em;
height: 1em; height: 1em;
background-color: #333; background-color: #333;

View File

@@ -174,12 +174,12 @@ textarea,
/* Add a gap between consecutive radios / check boxes */ /* Add a gap between consecutive radios / check boxes */
label.tn-radio + label.tn-radio, label.tn-radio + label.tn-radio,
label.tn-checkbox + label.tn-checkbox { label.tn-checkbox + label.tn-checkbox {
margin-left: 12px; margin-inline-start: 12px;
} }
label.tn-radio input[type="radio"], label.tn-radio input[type="radio"],
label.tn-checkbox input[type="checkbox"] { label.tn-checkbox input[type="checkbox"] {
margin-right: .5em; margin-inline-end: .5em;
} }
#left-pane input, #left-pane input,
@@ -226,7 +226,7 @@ samp {
.badge { .badge {
--bs-badge-color: var(--muted-text-color); --bs-badge-color: var(--muted-text-color);
margin-left: 8px; margin-inline-start: 8px;
background: var(--accented-background-color); background: var(--accented-background-color);
} }
@@ -338,8 +338,8 @@ button kbd {
} }
.ui-menu kbd { .ui-menu kbd {
margin-left: 30px; margin-inline-start: 30px;
float: right; float: inline-end;
} }
.suppressed { .suppressed {
@@ -392,7 +392,7 @@ body.desktop .tabulator-popup-container {
} }
.dropend .dropdown-toggle::after { .dropend .dropdown-toggle::after {
margin-left: 0.5em; margin-inline-start: 0.5em;
color: var(--muted-text-color); color: var(--muted-text-color);
} }
@@ -403,7 +403,7 @@ body.desktop .tabulator-popup-container {
.dropdown-menu .disabled .disabled-tooltip { .dropdown-menu .disabled .disabled-tooltip {
pointer-events: all; pointer-events: all;
margin-left: 8px; margin-inline-start: 8px;
font-size: 0.5em; font-size: 0.5em;
color: var(--disabled-tooltip-icon-color); color: var(--disabled-tooltip-icon-color);
cursor: help; cursor: help;
@@ -443,7 +443,7 @@ body #context-menu-container .dropdown-item > span {
.dropdown-item span.keyboard-shortcut, .dropdown-item span.keyboard-shortcut,
.dropdown-item *:not(.keyboard-shortcut) > kbd { .dropdown-item *:not(.keyboard-shortcut) > kbd {
flex-grow: 1; flex-grow: 1;
text-align: right; text-align: end;
padding-inline-start: 12px; padding-inline-start: 12px;
} }
@@ -504,7 +504,7 @@ body #context-menu-container .dropdown-item > span {
body .cm-editor .cm-gutters { body .cm-editor .cm-gutters {
background-color: inherit !important; background-color: inherit !important;
border-right: none; border-inline-end: none;
} }
body .cm-editor .cm-placeholder { body .cm-editor .cm-placeholder {
@@ -586,6 +586,10 @@ button.btn-sm {
z-index: 1000; z-index: 1000;
} }
body[dir=rtl] .ck.ck-block-toolbar-button {
transform: translateX(-7px);
}
pre:not(.hljs) { pre:not(.hljs) {
color: var(--main-text-color) !important; color: var(--main-text-color) !important;
white-space: pre-wrap; white-space: pre-wrap;
@@ -604,11 +608,11 @@ pre:not(.hljs) {
pre > button.copy-button { pre > button.copy-button {
position: absolute; position: absolute;
top: var(--copy-button-margin-size); top: var(--copy-button-margin-size);
right: var(--copy-button-margin-size); inset-inline-end: var(--copy-button-margin-size);
} }
:root pre:has(> button.copy-button) { :root pre:has(> button.copy-button) {
padding-right: calc(var(--copy-button-width) + (var(--copy-button-margin-size) * 2)); padding-inline-end: calc(var(--copy-button-width) + (var(--copy-button-margin-size) * 2));
} }
pre > button.copy-button:hover { pre > button.copy-button:hover {
@@ -634,31 +638,31 @@ pre > button.copy-button:active {
.full-text-search-button { .full-text-search-button {
cursor: pointer; cursor: pointer;
font-size: 1.3em; font-size: 1.3em;
padding-left: 5px; padding-inline-start: 5px;
padding-right: 5px; padding-inline-end: 5px;
} }
.input-clearer-button { .input-clearer-button {
cursor: pointer; cursor: pointer;
font-size: 1.3em; font-size: 1.3em;
background: inherit !important; background: inherit !important;
padding-left: 5px; padding-inline-start: 5px;
padding-right: 5px; padding-inline-end: 5px;
} }
.open-external-link-button { .open-external-link-button {
cursor: pointer; cursor: pointer;
font-size: 1.3em; font-size: 1.3em;
padding-left: 5px; padding-inline-start: 5px;
padding-right: 5px; padding-inline-end: 5px;
padding-top: 8px; padding-top: 8px;
} }
.go-to-selected-note-button { .go-to-selected-note-button {
cursor: pointer; cursor: pointer;
font-size: 1.3em; font-size: 1.3em;
padding-left: 4px; padding-inline-start: 4px;
padding-right: 3px; padding-inline-end: 3px;
} }
.go-to-selected-note-button.disabled, .go-to-selected-note-button.disabled,
@@ -671,7 +675,7 @@ pre > button.copy-button:active {
.note-autocomplete-input { .note-autocomplete-input {
/* this is for seamless integration of "input clearer" button */ /* this is for seamless integration of "input clearer" button */
border-right: 0; border-inline-end: 0;
} }
table.promoted-attributes-in-tooltip { table.promoted-attributes-in-tooltip {
@@ -704,10 +708,10 @@ table.promoted-attributes-in-tooltip th {
border-top-color: var(--main-border-color) !important; border-top-color: var(--main-border-color) !important;
} }
.bs-tooltip-left .tooltip-arrow::before { .bs-tooltip-left .tooltip-arrow::before {
border-left-color: var(--main-border-color) !important; border-inline-start-color: var(--main-border-color) !important;
} }
.bs-tooltip-right .tooltip-arrow::before { .bs-tooltip-right .tooltip-arrow::before {
border-right-color: var(--main-border-color) !important; border-inline-end-color: var(--main-border-color) !important;
} }
.bs-tooltip-bottom .tooltip-arrow::after { .bs-tooltip-bottom .tooltip-arrow::after {
@@ -717,17 +721,17 @@ table.promoted-attributes-in-tooltip th {
border-top-color: var(--tooltip-background-color) !important; border-top-color: var(--tooltip-background-color) !important;
} }
.bs-tooltip-left .tooltip-arrow::after { .bs-tooltip-left .tooltip-arrow::after {
border-left-color: var(--tooltip-background-color) !important; border-inline-start-color: var(--tooltip-background-color) !important;
} }
.bs-tooltip-right .tooltip-arrow::after { .bs-tooltip-right .tooltip-arrow::after {
border-right-color: var(--tooltip-background-color) !important; border-inline-end-color: var(--tooltip-background-color) !important;
} }
.bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow::before,
.bs-tooltip-left .tooltip-arrow::before { .bs-tooltip-left .tooltip-arrow::before {
left: -1px; inset-inline-start: -1px;
border-width: 0.4rem 0 0.4rem 0.4rem; border-width: 0.4rem 0 0.4rem 0.4rem;
border-left-color: var(--main-border-color) !important; border-inline-start-color: var(--main-border-color) !important;
} }
.bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow::before,
@@ -739,9 +743,9 @@ table.promoted-attributes-in-tooltip th {
.bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow::before,
.bs-tooltip-right .tooltip-arrow::before { .bs-tooltip-right .tooltip-arrow::before {
right: -1px; inset-inline-end: -1px;
border-width: 0.4rem 0.4rem 0.4rem 0; border-width: 0.4rem 0.4rem 0.4rem 0;
border-right-color: var(--main-border-color) !important; border-inline-end-color: var(--main-border-color) !important;
} }
.bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow::before,
@@ -753,9 +757,9 @@ table.promoted-attributes-in-tooltip th {
.bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow::after, .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow::after,
.bs-tooltip-left .tooltip-arrow::after { .bs-tooltip-left .tooltip-arrow::after {
left: -1px; inset-inline-start: -1px;
border-width: 0.4rem 0 0.4rem 0.4rem; border-width: 0.4rem 0 0.4rem 0.4rem;
border-left-color: var(--tooltip-background-color) !important; border-inline-start-color: var(--tooltip-background-color) !important;
} }
.bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow::after, .bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow::after,
@@ -767,9 +771,9 @@ table.promoted-attributes-in-tooltip th {
.bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow::after, .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow::after,
.bs-tooltip-right .tooltip-arrow::after { .bs-tooltip-right .tooltip-arrow::after {
right: -1px; inset-inline-end: -1px;
border-width: 0.4rem 0.4rem 0.4rem 0; border-width: 0.4rem 0.4rem 0.4rem 0;
border-right-color: var(--tooltip-background-color) !important; border-inline-end-color: var(--tooltip-background-color) !important;
} }
.bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow::after, .bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow::after,
@@ -788,7 +792,7 @@ table.promoted-attributes-in-tooltip th {
background-color: var(--tooltip-background-color) !important; background-color: var(--tooltip-background-color) !important;
border: 1px solid var(--main-border-color); border: 1px solid var(--main-border-color);
border-radius: 5px; border-radius: 5px;
text-align: left; text-align: start;
color: var(--main-text-color) !important; color: var(--main-text-color) !important;
max-width: 500px; max-width: 500px;
box-shadow: 10px 10px 93px -25px #aaaaaa; box-shadow: 10px 10px 93px -25px #aaaaaa;
@@ -821,7 +825,7 @@ table.promoted-attributes-in-tooltip th {
.note-tooltip-content .open-popup-button { .note-tooltip-content .open-popup-button {
position: absolute; position: absolute;
right: 15px; inset-inline-end: 15px;
bottom: 8px; bottom: 8px;
font-size: 1.2em; font-size: 1.2em;
color: inherit; color: inherit;
@@ -841,7 +845,7 @@ table.promoted-attributes-in-tooltip th {
} }
.tooltip-inner figure.image-style-side { .tooltip-inner figure.image-style-side {
float: right; float: inline-end;
} }
.tooltip.show { .tooltip.show {
@@ -890,7 +894,7 @@ table.promoted-attributes-in-tooltip th {
.aa-dropdown-menu .aa-suggestion .text { .aa-dropdown-menu .aa-suggestion .text {
display: inline-block; display: inline-block;
width: calc(100% - 20px); width: calc(100% - 20px);
padding-left: 4px; padding-inline-start: 4px;
} }
.aa-dropdown-menu .aa-suggestion .search-result-title { .aa-dropdown-menu .aa-suggestion .search-result-title {
@@ -916,7 +920,7 @@ table.promoted-attributes-in-tooltip th {
} }
.help-button { .help-button {
float: right; float: inline-end;
background: none; background: none;
font-weight: 900; font-weight: 900;
color: orange; color: orange;
@@ -1033,7 +1037,7 @@ svg.ck-icon .note-icon {
counter-increment: footnote-counter; counter-increment: footnote-counter;
display: flex; display: flex;
list-style: none; list-style: none;
margin-left: 0.5em; margin-inline-start: 0.5em;
} }
.ck-content .footnote-item > * { .ck-content .footnote-item > * {
@@ -1041,13 +1045,13 @@ svg.ck-icon .note-icon {
} }
.ck-content .footnote-back-link { .ck-content .footnote-back-link {
margin-right: 0.1em; margin-inline-end: 0.1em;
position: relative; position: relative;
top: -0.2em; top: -0.2em;
} }
.ck-content .footnotes .footnote-back-link > sup { .ck-content .footnotes .footnote-back-link > sup {
margin-right: 0; margin-inline-end: 0;
} }
.ck-content .footnote-item:before { .ck-content .footnote-item:before {
@@ -1055,8 +1059,8 @@ svg.ck-icon .note-icon {
display: inline-block; display: inline-block;
min-width: fit-content; min-width: fit-content;
position: relative; position: relative;
right: 0.2em; inset-inline-end: 0.2em;
text-align: right; text-align: end;
} }
.ck-content .footnote-content { .ck-content .footnote-content {
@@ -1072,11 +1076,11 @@ svg.ck-icon .note-icon {
} }
#options-dialog input[type="number"] { #options-dialog input[type="number"] {
text-align: right; text-align: end;
} }
.help-cards ul { .help-cards ul {
padding-left: 20px; padding-inline-start: 20px;
} }
.help-cards kbd { .help-cards kbd {
@@ -1153,8 +1157,8 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
} }
.toast.no-title .toast-body { .toast.no-title .toast-body {
padding-left: 0; padding-inline-start: 0;
padding-right: 0; padding-inline-end: 0;
} }
.toast.no-title .toast-header { .toast.no-title .toast-header {
@@ -1274,8 +1278,8 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
#context-menu-cover.show { #context-menu-cover.show {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
bottom: 0; bottom: 0;
z-index: 1000; z-index: 1000;
background: rgba(0, 0, 0, 0.1); background: rgba(0, 0, 0, 0.1);
@@ -1288,8 +1292,8 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
body.mobile #context-menu-container.mobile-bottom-menu { body.mobile #context-menu-container.mobile-bottom-menu {
position: fixed !important; position: fixed !important;
left: 0 !important; inset-inline-start: 0 !important;
right: 0 !important; inset-inline-end: 0 !important;
bottom: 0 !important; bottom: 0 !important;
top: unset !important; top: unset !important;
max-height: 70vh; max-height: 70vh;
@@ -1339,7 +1343,7 @@ body.desktop li.dropdown-submenu:hover > ul.dropdown-menu {
.dropdown-submenu > .dropdown-menu { .dropdown-submenu > .dropdown-menu {
top: 0; top: 0;
left: calc(100% - 2px); /* -2px, otherwise there's a small gap between menu and submenu where the hover can disappear */ inset-inline-start: calc(100% - 2px); /* -2px, otherwise there's a small gap between menu and submenu where the hover can disappear */
margin-top: -10px; margin-top: -10px;
min-width: 15rem; min-width: 15rem;
/* to make submenu scrollable https://github.com/zadam/trilium/issues/3136 */ /* to make submenu scrollable https://github.com/zadam/trilium/issues/3136 */
@@ -1348,7 +1352,7 @@ body.desktop li.dropdown-submenu:hover > ul.dropdown-menu {
} }
body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu { body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
left: calc(-100% + 10px); inset-inline-start: calc(-100% + 10px);
} }
.right-dropdown-widget { .right-dropdown-widget {
@@ -1414,7 +1418,7 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
.ck.ck-slash-command-button__text-part, .ck.ck-slash-command-button__text-part,
.ck.ck-template-form__text-part { .ck.ck-template-form__text-part {
margin-left: 0.5em; margin-inline-start: 0.5em;
line-height: 1.2em !important; line-height: 1.2em !important;
} }
@@ -1445,8 +1449,8 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
} }
.area-expander-text { .area-expander-text {
padding-left: 20px; padding-inline-start: 20px;
padding-right: 20px; padding-inline-end: 20px;
white-space: nowrap; white-space: nowrap;
} }
@@ -1533,16 +1537,16 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
position: fixed !important; position: fixed !important;
bottom: calc(var(--mobile-bottom-offset) + var(--launcher-pane-size)) !important; bottom: calc(var(--mobile-bottom-offset) + var(--launcher-pane-size)) !important;
top: unset !important; top: unset !important;
left: 0 !important; inset-inline-start: 0 !important;
right: 0 !important; inset-inline-end: 0 !important;
transform: unset !important; transform: unset !important;
} }
#mobile-sidebar-container { #mobile-sidebar-container {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
bottom: 0; bottom: 0;
z-index: 1000; z-index: 1000;
transition: background-color 250ms ease-in-out; transition: background-color 250ms ease-in-out;
@@ -1557,7 +1561,7 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
#mobile-sidebar-wrapper { #mobile-sidebar-wrapper {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
bottom: 0; bottom: 0;
width: 85vw; width: 85vw;
padding-top: env(safe-area-inset-top); padding-top: env(safe-area-inset-top);
@@ -1589,8 +1593,8 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
body.mobile .modal-dialog { body.mobile .modal-dialog {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
margin: 0 !important; margin: 0 !important;
max-height: 85vh; max-height: 85vh;
display: flex; display: flex;
@@ -1748,8 +1752,8 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
display: flex; display: flex;
flex-shrink: 0; flex-shrink: 0;
flex-direction: column; flex-direction: column;
margin-left: 10px; margin-inline-start: 10px;
margin-right: 5px; margin-inline-end: 5px;
} }
#right-pane .card-header { #right-pane .card-header {
@@ -1789,7 +1793,7 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
} }
#right-pane .card-body ul { #right-pane .card-body ul {
padding-left: 25px; padding-inline-start: 25px;
margin-bottom: 5px; margin-bottom: 5px;
} }
@@ -1800,8 +1804,8 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
} }
.note-split { .note-split {
margin-left: auto; margin-inline-start: auto;
margin-right: auto; margin-inline-end: auto;
} }
.note-split.full-content-width { .note-split.full-content-width {
@@ -1820,7 +1824,7 @@ button.close:hover {
.reference-link .bx { .reference-link .bx {
position: relative; position: relative;
top: 1px; top: 1px;
margin-right: 3px; margin-inline-end: 3px;
} }
.options-section:first-of-type h4 { .options-section:first-of-type h4 {
@@ -1858,7 +1862,7 @@ textarea {
.attachment-help-button { .attachment-help-button {
display: inline-block; display: inline-block;
margin-left: 10px; margin-inline-start: 10px;
vertical-align: middle; vertical-align: middle;
font-size: 1em; font-size: 1em;
} }
@@ -1966,7 +1970,7 @@ textarea {
} }
body.electron.platform-darwin:not(.native-titlebar) .tab-row-container { body.electron.platform-darwin:not(.native-titlebar) .tab-row-container {
padding-left: 1em; padding-inline-start: 1em;
} }
#tab-row-left-spacer { #tab-row-left-spacer {
@@ -1975,7 +1979,7 @@ body.electron.platform-darwin:not(.native-titlebar) .tab-row-container {
} }
.tab-row-widget { .tab-row-widget {
padding-right: calc(100vw - env(titlebar-area-width, 100vw)); padding-inline-end: calc(100vw - env(titlebar-area-width, 100vw));
} }
.tab-row-container .toggle-button { .tab-row-container .toggle-button {
@@ -2038,7 +2042,7 @@ body.zen .split-note-container-widget > .gutter {
body.zen #launcher-pane { body.zen #launcher-pane {
position: absolute !important; position: absolute !important;
top: 0 !important; top: 0 !important;
right: 0 !important; inset-inline-end: 0 !important;
width: 64px !important; width: 64px !important;
height: 64px !important; height: 64px !important;
background: transparent !important; background: transparent !important;
@@ -2049,8 +2053,8 @@ body.zen .title-row {
display: block !important; display: block !important;
height: unset !important; height: unset !important;
-webkit-app-region: drag; -webkit-app-region: drag;
padding-left: env(titlebar-area-x); padding-inline-start: env(titlebar-area-x);
padding-right: calc(100vw - env(titlebar-area-width, 100vw) + 2.5em); padding-inline-end: calc(100vw - env(titlebar-area-width, 100vw) + 2.5em);
} }
body.zen .floating-buttons { body.zen .floating-buttons {
@@ -2058,7 +2062,7 @@ body.zen .floating-buttons {
} }
body.zen .floating-buttons-children { body.zen .floating-buttons-children {
right: 0; inset-inline-end: 0;
} }
body.zen .floating-buttons-children .button-widget { body.zen .floating-buttons-children .button-widget {
@@ -2217,7 +2221,7 @@ footer.webview-footer button {
.chat-input { .chat-input {
width: 100%; width: 100%;
resize: none; resize: none;
padding-right: 40px; padding-inline-end: 40px;
} }
.chat-buttons { .chat-buttons {
@@ -2280,7 +2284,7 @@ footer.webview-footer button {
padding: 1em; padding: 1em;
margin: 1.25em 0; margin: 1.25em 0;
position: relative; position: relative;
padding-left: 2.5em; padding-inline-start: 2.5em;
overflow: hidden; overflow: hidden;
} }
@@ -2293,7 +2297,7 @@ footer.webview-footer button {
font-family: boxicons !important; font-family: boxicons !important;
position: absolute; position: absolute;
top: 1em; top: 1em;
left: 1em; inset-inline-start: 1em;
} }
.admonition.note { --accent-color: var(--admonition-note-accent-color); } .admonition.note { --accent-color: var(--admonition-note-accent-color); }
@@ -2319,18 +2323,18 @@ footer.webview-footer button {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 0.9em; font-size: 0.9em;
margin-right: 15px; margin-inline-end: 15px;
cursor: pointer; cursor: pointer;
} }
.chat-option input[type="checkbox"] { .chat-option input[type="checkbox"] {
margin-right: 5px; margin-inline-end: 5px;
} }
/* Style for thinking process in chat responses */ /* Style for thinking process in chat responses */
.thinking-process { .thinking-process {
background-color: rgba(0, 0, 0, 0.05); background-color: rgba(0, 0, 0, 0.05);
border-left: 3px solid var(--main-text-color); border-inline-start: 3px solid var(--main-text-color);
padding: 10px; padding: 10px;
margin: 10px 0; margin: 10px 0;
border-radius: 4px; border-radius: 4px;
@@ -2338,23 +2342,23 @@ footer.webview-footer button {
.thinking-step { .thinking-step {
margin-bottom: 8px; margin-bottom: 8px;
padding-left: 10px; padding-inline-start: 10px;
} }
.thinking-step.observation { .thinking-step.observation {
border-left: 2px solid #69c7ff; border-inline-start: 2px solid #69c7ff;
} }
.thinking-step.hypothesis { .thinking-step.hypothesis {
border-left: 2px solid #9839f7; border-inline-start: 2px solid #9839f7;
} }
.thinking-step.evidence { .thinking-step.evidence {
border-left: 2px solid #40c025; border-inline-start: 2px solid #40c025;
} }
.thinking-step.conclusion { .thinking-step.conclusion {
border-left: 2px solid #e2aa03; border-inline-start: 2px solid #e2aa03;
font-weight: bold; font-weight: bold;
} }
@@ -2369,17 +2373,17 @@ footer.webview-footer button {
.content-floating-buttons.top-left { .content-floating-buttons.top-left {
top: 10px; top: 10px;
left: 10px; inset-inline-start: 10px;
} }
.content-floating-buttons.bottom-left { .content-floating-buttons.bottom-left {
bottom: 10px; bottom: 10px;
left: 10px; inset-inline-start: 10px;
} }
.content-floating-buttons.bottom-right { .content-floating-buttons.bottom-right {
bottom: 10px; bottom: 10px;
right: 10px; inset-inline-end: 10px;
} }
.content-floating-buttons button.bx { .content-floating-buttons button.bx {

View File

@@ -67,13 +67,13 @@
} }
.tabulator div.tabulator-header .tabulator-frozen.tabulator-frozen-left { .tabulator div.tabulator-header .tabulator-frozen.tabulator-frozen-left {
margin-left: var(--cell-editing-border-width); margin-inline-start: var(--cell-editing-border-width);
} }
.tabulator div.tabulator-header .tabulator-col, .tabulator div.tabulator-header .tabulator-col,
.tabulator div.tabulator-header .tabulator-frozen.tabulator-frozen-left { .tabulator div.tabulator-header .tabulator-frozen.tabulator-frozen-left {
background: var(--col-header-background-color); background: var(--col-header-background-color);
border-right: var(--col-header-separator-border); border-inline-end: var(--col-header-separator-border);
} }
/* Table body */ /* Table body */
@@ -90,8 +90,8 @@
} }
.tabulator-row .tabulator-cell input { .tabulator-row .tabulator-cell input {
padding-left: var(--cell-horiz-padding-size) !important; padding-inline-start: var(--cell-horiz-padding-size) !important;
padding-right: var(--cell-horiz-padding-size) !important; padding-inline-end: var(--cell-horiz-padding-size) !important;
} }
.tabulator-row { .tabulator-row {
@@ -117,12 +117,12 @@
/* Cell */ /* Cell */
.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left { .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left {
margin-right: var(--cell-editing-border-width); margin-inline-end: var(--cell-editing-border-width);
} }
.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left, .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left,
.tabulator-row .tabulator-cell { .tabulator-row .tabulator-cell {
border-right-color: transparent; border-inline-end-color: transparent;
} }
.tabulator-row .tabulator-cell:not(.tabulator-editable) { .tabulator-row .tabulator-cell:not(.tabulator-editable) {
@@ -156,14 +156,14 @@
/* Align items without children/expander to the ones with. */ /* Align items without children/expander to the ones with. */
.tabulator-cell[tabulator-field="title"] > span:first-child, /* 1st level */ .tabulator-cell[tabulator-field="title"] > span:first-child, /* 1st level */
.tabulator-cell[tabulator-field="title"] > div:first-child + span { /* sub-level */ .tabulator-cell[tabulator-field="title"] > div:first-child + span { /* sub-level */
padding-left: 21px; padding-inline-start: 21px;
} }
/* Checkbox cells */ /* Checkbox cells */
.tabulator .tabulator-cell:has(svg), .tabulator .tabulator-cell:has(svg),
.tabulator .tabulator-cell:has(input[type="checkbox"]) { .tabulator .tabulator-cell:has(input[type="checkbox"]) {
padding-left: 8px; padding-inline-start: 8px;
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;

View File

@@ -120,8 +120,8 @@ body.desktop .dropdown-menu::before,
border-radius: var(--dropdown-border-radius); border-radius: var(--dropdown-border-radius);
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
bottom: 0; bottom: 0;
z-index: -1; z-index: -1;
} }
@@ -211,7 +211,7 @@ html body .dropdown-item[disabled] {
.dropdown-item span.keyboard-shortcut { .dropdown-item span.keyboard-shortcut {
color: var(--menu-item-keyboard-shortcut-color) !important; color: var(--menu-item-keyboard-shortcut-color) !important;
margin-left: 16px; margin-inline-start: 16px;
} }
.dropdown-divider { .dropdown-divider {
@@ -224,8 +224,8 @@ html body .dropdown-item[disabled] {
position: absolute; position: absolute;
content: ""; content: "";
top: -1px; top: -1px;
left: calc(0px - var(--menu-padding-size)); inset-inline-start: calc(0px - var(--menu-padding-size));
right: calc(0px - var(--menu-padding-size)); inset-inline-end: calc(0px - var(--menu-padding-size));
border-top: 1px solid var(--menu-item-delimiter-color); border-top: 1px solid var(--menu-item-delimiter-color);
} }
@@ -237,7 +237,7 @@ html body .dropdown-item[disabled] {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
top: 0; top: 0;
right: 0; inset-inline-end: 0;
margin: unset !important; margin: unset !important;
border: unset !important; border: unset !important;
padding: 0 4px; padding: 0 4px;
@@ -246,6 +246,10 @@ html body .dropdown-item[disabled] {
color: var(--menu-item-arrow-color) !important; color: var(--menu-item-arrow-color) !important;
} }
body[dir=rtl] .dropdown-menu:not([data-popper-placement="bottom-start"]) .dropdown-toggle::after {
content: "\ea4d" !important;
}
/* Menu item group heading */ /* Menu item group heading */
/* The heading body */ /* The heading body */
@@ -264,8 +268,8 @@ html body .dropdown-item[disabled] {
content: ""; content: "";
position: absolute; position: absolute;
bottom: 8px; bottom: 8px;
left: calc(0px - var(--menu-padding-size)); inset-inline-start: calc(0px - var(--menu-padding-size));
right: calc(0px - var(--menu-padding-size)); inset-inline-end: calc(0px - var(--menu-padding-size));
border-top: 1px solid var(--menu-item-delimiter-color); border-top: 1px solid var(--menu-item-delimiter-color);
} }
@@ -357,13 +361,13 @@ li.dropdown-item a.dropdown-item-button:focus-visible {
} }
#toast-container .toast:not(.no-title) .bx { #toast-container .toast:not(.no-title) .bx {
margin-right: 0.5em; margin-inline-end: 0.5em;
font-size: 1.1em; font-size: 1.1em;
opacity: 0.85; opacity: 0.85;
} }
#toast-container .toast.no-title .bx { #toast-container .toast.no-title .bx {
margin-right: 0; margin-inline-end: 0;
font-size: 1.3em; font-size: 1.3em;
} }

View File

@@ -29,7 +29,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
margin-left: 8px; margin-inline-start: 8px;
border: 0; border: 0;
border-radius: 50%; border-radius: 50%;
padding: 0; padding: 0;
@@ -56,7 +56,7 @@
} }
.modal .modal-header .help-button { .modal .modal-header .help-button {
margin-right: 0; margin-inline-end: 0;
font-size: calc(var(--modal-control-button-size) * .75); font-size: calc(var(--modal-control-button-size) * .75);
font-family: unset; font-family: unset;
font-weight: bold; font-weight: bold;
@@ -141,7 +141,7 @@ div.tn-tool-dialog {
/* Search box wrapper */ /* Search box wrapper */
.jump-to-note-dialog .input-group { .jump-to-note-dialog .input-group {
margin-right: 16px; margin-inline-end: 16px;
} }
.jump-to-note-dialog .input-group:hover { .jump-to-note-dialog .input-group:hover {
@@ -197,8 +197,8 @@ div.tn-tool-dialog {
border: unset; border: unset;
padding-top: var(--timeline-item-top-padding); padding-top: var(--timeline-item-top-padding);
padding-bottom: var(--timeline-item-bottom-padding); padding-bottom: var(--timeline-item-bottom-padding);
padding-left: calc(var(--timeline-left-gap) + var(--timeline-right-gap)); padding-inline-start: calc(var(--timeline-left-gap) + var(--timeline-right-gap));
padding-right: var(--timeline-left-gap); padding-inline-end: var(--timeline-left-gap);
color: var(--active-item-text-color); color: var(--active-item-text-color);
} }
@@ -259,7 +259,7 @@ div.tn-tool-dialog {
position: absolute; position: absolute;
content: ""; content: "";
top: var(--connector-top, 0); top: var(--connector-top, 0);
left: calc(var(--timeline-left-gap) + ((var(--timeline-bullet-size) - var(--timeline-connector-size)) / 2)); inset-inline-start: calc(var(--timeline-left-gap) + ((var(--timeline-bullet-size) - var(--timeline-connector-size)) / 2));
bottom: var(--connector-bottom, 0); bottom: var(--connector-bottom, 0);
width: var(--timeline-connector-size); width: var(--timeline-connector-size);
border-radius: var(--connector-radius, 0) var(--connector-radius, 0) 0 0; border-radius: var(--connector-radius, 0) var(--connector-radius, 0) 0 0;
@@ -291,7 +291,7 @@ div.tn-tool-dialog {
position: absolute; position: absolute;
content: ""; content: "";
top: calc(var(--timeline-item-top-padding) + var(--timeline-bullet-vertical-pos)); top: calc(var(--timeline-item-top-padding) + var(--timeline-bullet-vertical-pos));
left: var(--timeline-left-gap); inset-inline-start: var(--timeline-left-gap);
width: var(--timeline-bullet-size); width: var(--timeline-bullet-size);
height: var(--timeline-bullet-size); height: var(--timeline-bullet-size);
border-radius: 50%; border-radius: 50%;
@@ -374,7 +374,7 @@ div.tn-tool-dialog {
} }
.help-dialog .help-cards kbd:first-child { .help-dialog .help-cards kbd:first-child {
margin-left: 0; margin-inline-start: 0;
} }
/* Inline code - used for Markdown samples */ /* Inline code - used for Markdown samples */
@@ -392,7 +392,7 @@ div.tn-tool-dialog {
} }
.delete-notes-list .note-path { .delete-notes-list .note-path {
padding-left: 8px; padding-inline-end: 8px;
} }
/* /*
@@ -401,7 +401,7 @@ div.tn-tool-dialog {
/* Labels */ /* Labels */
.attr-edit-table th { .attr-edit-table th {
padding-right: 12px; padding-inline-end: 12px;
font-weight: normal; font-weight: normal;
white-space: nowrap; white-space: nowrap;
} }
@@ -419,5 +419,5 @@ div.tn-tool-dialog {
} }
.note-type-chooser-dialog div.note-type-dropdown .dropdown-item span.bx { .note-type-chooser-dialog div.note-type-dropdown .dropdown-item span.bx {
margin-right: .25em; margin-inline-end: .25em;
} }

View File

@@ -62,7 +62,7 @@ button.btn.btn-secondary span.bx,
button.btn.btn-sm span.bx, button.btn.btn-sm span.bx,
button.btn.btn-success span.bx { button.btn.btn-success span.bx {
color: var(--cmd-button-icon-color); color: var(--cmd-button-icon-color);
padding-right: 0.35em; padding-inline-end: 0.35em;
font-size: 1.2em; font-size: 1.2em;
} }
@@ -71,7 +71,7 @@ button.btn.btn-primary kbd,
button.btn.btn-secondary kbd, button.btn.btn-secondary kbd,
button.btn.btn-sm kbd, button.btn.btn-sm kbd,
button.btn.btn-success kbd { button.btn.btn-success kbd {
margin-left: 0.5em; margin-inline-start: 0.5em;
background: var(--cmd-button-keyboard-shortcut-background); background: var(--cmd-button-keyboard-shortcut-background);
color: var(--cmd-button-keyboard-shortcut-color); color: var(--cmd-button-keyboard-shortcut-color);
font-size: 0.6em; font-size: 0.6em;
@@ -102,7 +102,7 @@ button.btn.btn-success kbd {
} }
.btn-group .tn-tool-button + .tn-tool-button { .btn-group .tn-tool-button + .tn-tool-button {
margin-left: 4px !important; margin-inline-start: 4px !important;
} }
/* The "x" icon button */ /* The "x" icon button */
@@ -237,7 +237,7 @@ input::selection,
outline-offset: 6px; outline-offset: 6px;
background: var(--input-background-color); background: var(--input-background-color);
border-radius: 6px; border-radius: 6px;
padding-right: 8px; padding-inline-end: 8px;
color: var(--quick-search-color); color: var(--quick-search-color);
flex-wrap: nowrap; flex-wrap: nowrap;
} }
@@ -357,13 +357,20 @@ select.form-control,
outline: 3px solid transparent; outline: 3px solid transparent;
outline-offset: 6px; outline-offset: 6px;
padding-right: calc(15px + 1.5rem); padding-inline-end: calc(15px + 1.5rem);
background: var(--input-background-color) var(--dropdown-arrow); background: var(--input-background-color) var(--dropdown-arrow);;
color: var(--input-text-color); color: var(--input-text-color);
border: unset; border: unset;
border-radius: 0.375rem; border-radius: 0.375rem;
} }
body[dir=rtl] select,
body[dir=rtl] select.form-select,
body[dir=rtl] select.form-control,
body[dir=rtl] .select-button.dropdown-toggle.btn {
background-position: left 0.75rem center;
}
select:hover, select:hover,
select.form-select:hover, select.form-select:hover,
select.form-control:hover, select.form-control:hover,
@@ -444,7 +451,7 @@ optgroup {
content: "\eae1"; content: "\eae1";
width: 2em; width: 2em;
height: 100%; height: 100%;
right: 0; inset-inline-end: 0;
top: 0; top: 0;
font-size: 1.2em; font-size: 1.2em;
font-family: boxicons; font-family: boxicons;
@@ -462,7 +469,7 @@ optgroup {
--box-label-gap: 0.5em; --box-label-gap: 0.5em;
position: relative; position: relative;
padding-left: calc(var(--box-size) + var(--box-label-gap)) !important; padding-inline-start: calc(var(--box-size) + var(--box-label-gap)) !important;
user-select: none; user-select: none;
} }
@@ -471,7 +478,7 @@ optgroup {
label.tn-checkbox > input[type="checkbox"] { label.tn-checkbox > input[type="checkbox"] {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
width: var(--box-size); width: var(--box-size);
height: 100%; height: 100%;
margin: unset; margin: unset;
@@ -485,7 +492,7 @@ optgroup {
content: ""; content: "";
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 0; inset-inline-start: 0;
translate: 0 -50%; translate: 0 -50%;
width: var(--box-size); width: var(--box-size);
height: var(--box-size); height: var(--box-size);

View File

@@ -19,11 +19,11 @@
} }
.chat-message.user-message { .chat-message.user-message {
margin-left: auto; margin-inline-start: auto;
} }
.chat-message.assistant-message { .chat-message.assistant-message {
margin-right: auto; margin-inline-end: auto;
} }
.message-avatar { .message-avatar {
@@ -33,7 +33,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-right: 8px; margin-inline-end: 8px;
} }
.user-message .message-avatar { .user-message .message-avatar {

View File

@@ -117,9 +117,9 @@
content: ""; content: "";
position: absolute; position: absolute;
top: var(--negative-padding); top: var(--negative-padding);
right: var(--negative-padding); inset-inline-end: var(--negative-padding);
bottom: var(--negative-padding); bottom: var(--negative-padding);
left: var(--negative-padding); inset-inline-start: var(--negative-padding);
border-radius: var(--dropdown-border-radius); border-radius: var(--dropdown-border-radius);
backdrop-filter: var(--dropdown-backdrop-filter); backdrop-filter: var(--dropdown-backdrop-filter);
z-index: -1; z-index: -1;
@@ -210,7 +210,7 @@
/* Separator */ /* Separator */
:root .ck .ck-list__separator { :root .ck .ck-list__separator {
margin: .5em 0; margin: .5em 0;
margin-left: calc(0px - var(--ck-editor-popup-padding)); margin-inline-start: calc(0px - var(--ck-editor-popup-padding));
width: calc(100% + (var(--ck-editor-popup-padding) * 2)); width: calc(100% + (var(--ck-editor-popup-padding) * 2));
background: var(--menu-item-delimiter-color); background: var(--menu-item-delimiter-color);
} }
@@ -233,8 +233,8 @@
position: absolute; position: absolute;
top: 0; top: 0;
bottom: var(--negative-padding); bottom: var(--negative-padding);
left: var(--negative-padding); inset-inline-start: var(--negative-padding);
right: var(--negative-padding); inset-inline-end: var(--negative-padding);
border-top: 1px solid var(--ck-editor-popup-border-color); border-top: 1px solid var(--ck-editor-popup-border-color);
background: var(--menu-section-background-color); background: var(--menu-section-background-color);
} }
@@ -255,7 +255,7 @@
:root .ck.ck-toolbar .ck.ck-toolbar__separator { :root .ck.ck-toolbar .ck.ck-toolbar__separator {
background: transparent; background: transparent;
border-left: 1px solid var(--ck-color-toolbar-border); border-inline-start: 1px solid var(--ck-color-toolbar-border);
} }
/* The last separator of the toolbar */ /* The last separator of the toolbar */
@@ -354,7 +354,7 @@
align-items: center; align-items: center;
width: 100%; width: 100%;
margin: 4px; margin: 4px;
padding-right: 2em; padding-inline-end: 2em;
border: 1px solid var(--accent); border: 1px solid var(--accent);
border-radius: 6px; border-radius: 6px;
} }
@@ -492,7 +492,7 @@ button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel).ck
/* Move the label above the text box regardless of the text box state */ /* Move the label above the text box regardless of the text box state */
transform: translate(0, calc(-.2em - var(--ck-input-label-height))) !important; transform: translate(0, calc(-.2em - var(--ck-input-label-height))) !important;
padding-left: 0 !important; padding-inline-start: 0 !important;
background: transparent; background: transparent;
font-size: .85em; font-size: .85em;
font-weight: 600; font-weight: 600;
@@ -556,7 +556,7 @@ pre button.copy-button.icon-action {
} }
:root pre:has(> button.copy-button) { :root pre:has(> button.copy-button) {
padding-right: calc(var(--icon-button-size) + (var(--copy-button-margin-size) * 2)); padding-inline-end: calc(var(--icon-button-size) + (var(--copy-button-margin-size) * 2));
} }
html .note-detail-editable-text :not(figure, .include-note, hr):first-child { html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
@@ -615,12 +615,12 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
.ck-content blockquote:before { .ck-content blockquote:before {
content: "“"; content: "“";
left: 0.2em; inset-inline-start: 0.2em;
} }
.ck-content blockquote:after { .ck-content blockquote:after {
content: "”"; content: "”";
right: 0.35em; inset-inline-end: 0.35em;
} }
.ck-content h2, .ck-content h2,

View File

@@ -52,7 +52,7 @@
background-color: #f5f5f5; background-color: #f5f5f5;
} }
.google-login-btn img { .google-login-btn img {
margin-right: 10px; margin-inline-end: 10px;
width: 18px; width: 18px;
height: 18px; height: 18px;
} }
@@ -199,7 +199,7 @@ body.desktop .option-section:not(.tn-no-card) {
color: var(--launcher-pane-text-color); color: var(--launcher-pane-text-color);
margin-top: calc(-1 * var(--options-card-padding) - var(--options-title-font-size) - var(--options-title-offset)) !important; margin-top: calc(-1 * var(--options-card-padding) - var(--options-title-font-size) - var(--options-title-offset)) !important;
margin-bottom: calc(var(--options-title-offset) + var(--options-card-padding)) !important; margin-bottom: calc(var(--options-title-offset) + var(--options-card-padding)) !important;
margin-left: calc(-1 * var(--options-card-padding)); margin-inline-start: calc(-1 * var(--options-card-padding));
} }
.options-section:not(.tn-no-card) h5 { .options-section:not(.tn-no-card) h5 {
@@ -216,8 +216,8 @@ body.desktop .option-section:not(.tn-no-card) {
.options-section hr { .options-section hr {
--bs-border-width: 2px; --bs-border-width: 2px;
margin-left: calc(var(--options-card-padding) * -1); margin-inline-start: calc(var(--options-card-padding) * -1);
margin-right: calc(var(--options-card-padding) * -1); margin-inline-end: calc(var(--options-card-padding) * -1);
opacity: 1; opacity: 1;
color: var(--root-background); color: var(--root-background);
} }

View File

@@ -95,7 +95,7 @@ div.promoted-attributes-container {
/* Note type dropdown */ /* Note type dropdown */
ul.note-type-dropdown .check { ul.note-type-dropdown .check {
margin-right: 6px; margin-inline-end: 6px;
} }
ul.note-type-dropdown li.dropdown-item { ul.note-type-dropdown li.dropdown-item {
@@ -105,7 +105,7 @@ ul.note-type-dropdown li.dropdown-item {
/* Editability dropdown */ /* Editability dropdown */
ul.editability-dropdown li.dropdown-item > div { ul.editability-dropdown li.dropdown-item > div {
margin-left: 4px; margin-inline-start: 4px;
} }
.editability-dropdown .dropdown-item .description { .editability-dropdown .dropdown-item .description {
@@ -142,12 +142,12 @@ ul.editability-dropdown li.dropdown-item > div {
} }
.attribute-list .save-attributes-button { .attribute-list .save-attributes-button {
right: 30px; inset-inline-end: 30px;
} }
/* Note path in attribute detail dialog */ /* Note path in attribute detail dialog */
.attr-detail .note-path { .attr-detail .note-path {
margin-left: 8px; margin-inline-start: 8px;
} }
/* /*

View File

@@ -69,7 +69,7 @@ body.background-effects.platform-win32.layout-vertical #vertical-main-container
/* Add a border to the vertical launch bar if collapsed. */ /* Add a border to the vertical launch bar if collapsed. */
body.layout-vertical #horizontal-main-container.left-pane-hidden #launcher-pane.vertical { body.layout-vertical #horizontal-main-container.left-pane-hidden #launcher-pane.vertical {
border-right: 2px solid var(--left-pane-collapsed-border-color); border-inline-end: 2px solid var(--left-pane-collapsed-border-color);
} }
body.background-effects.zen #root-widget { body.background-effects.zen #root-widget {
@@ -284,7 +284,7 @@ body.layout-horizontal > .horizontal {
} }
#launcher-pane.horizontal .global-menu-button .global-menu-button-update-available { #launcher-pane.horizontal .global-menu-button .global-menu-button-update-available {
right: -23px; inset-inline-end: -23px;
bottom: -22px; bottom: -22px;
transform: scale(0.85); transform: scale(0.85);
} }
@@ -400,9 +400,9 @@ body.layout-horizontal > .horizontal {
content: ""; content: "";
position: absolute; position: absolute;
top: var(--vertical-margin); top: var(--vertical-margin);
right: var(--horiz-margin); inset-inline-end: var(--horiz-margin);
bottom: var(--vertical-margin); bottom: var(--vertical-margin);
left: var(--horiz-margin); inset-inline-start: var(--horiz-margin);
border-radius: 6px; border-radius: 6px;
background: var(--calendar-day-highlight-background); background: var(--calendar-day-highlight-background);
z-index: -1; z-index: -1;
@@ -450,7 +450,7 @@ div.bookmark-folder-widget .note-link:hover {
} }
div.bookmark-folder-widget .note-link a { div.bookmark-folder-widget .note-link a {
padding-left: 8px; padding-inline-start: 8px;
color: var(--menu-text-color); color: var(--menu-text-color);
cursor: default; cursor: default;
} }
@@ -471,8 +471,8 @@ div.bookmark-folder-widget .note-link .bx {
div.quick-search { div.quick-search {
--padding-top: 8px; --padding-top: 8px;
--padding-left: 8px; --padding-inline-start: 8px;
--padding-right: 8px; --padding-inline-end: 8px;
--padding-bottom: 8px; --padding-bottom: 8px;
position: relative; position: relative;
@@ -480,7 +480,7 @@ div.quick-search {
align-items: center; align-items: center;
height: unset; height: unset;
contain: unset; contain: unset;
padding: var(--padding-top) var(--padding-right) var(--padding-bottom) var(--padding-left); padding: var(--padding-top) var(--padding-inline-end) var(--padding-bottom) var(--padding-inline-start);
} }
div.quick-search, div.quick-search,
@@ -496,9 +496,9 @@ div.quick-search::before {
position: absolute; position: absolute;
content: ""; content: "";
top: var(--padding-top); top: var(--padding-top);
left: var(--padding-left); inset-inline-start: var(--padding-inline-start);
bottom: var(--padding-bottom); bottom: var(--padding-bottom);
right: var(--padding-right); inset-inline-end: var(--padding-inline-end);
z-index: 0; z-index: 0;
border: 2px solid transparent; border: 2px solid transparent;
border-radius: 6px; border-radius: 6px;
@@ -520,7 +520,7 @@ div.quick-search:focus-within:before {
} }
div.quick-search input { div.quick-search input {
padding-left: 15px !important; padding-inline-start: 15px !important;
box-shadow: unset !important; box-shadow: unset !important;
background: transparent !important; background: transparent !important;
} }
@@ -539,7 +539,7 @@ div.quick-search .search-button {
justify-content: center; justify-content: center;
width: 25px; width: 25px;
height: 25px; height: 25px;
margin-right: 8px; margin-inline-end: 8px;
border-radius: 50%; border-radius: 50%;
padding: 0; padding: 0;
color: var(--quick-search-color) !important; color: var(--quick-search-color) !important;
@@ -631,12 +631,12 @@ body.layout-vertical.background-effects div.quick-search .dropdown-menu {
} }
#left-pane .ui-fancytree ul { #left-pane .ui-fancytree ul {
padding-left: 10px; padding-inline-start: 10px;
} }
/* The root element of the tree */ /* The root element of the tree */
#left-pane .fancytree-container > li:first-child > span { #left-pane .fancytree-container > li:first-child > span {
padding-left: 12px; padding-inline-start: 12px;
} }
#left-pane span.fancytree-node.fancytree-active { #left-pane span.fancytree-node.fancytree-active {
@@ -658,9 +658,9 @@ body.layout-vertical.background-effects div.quick-search .dropdown-menu {
position: absolute; position: absolute;
content: ""; content: "";
top: var(--left-pane-item-selected-shadow-size); top: var(--left-pane-item-selected-shadow-size);
left: var(--left-pane-item-selected-shadow-size); inset-inline-start: var(--left-pane-item-selected-shadow-size);
bottom: var(--left-pane-item-selected-shadow-size); bottom: var(--left-pane-item-selected-shadow-size);
right: var(--left-pane-item-selected-shadow-size); inset-inline-end: var(--left-pane-item-selected-shadow-size);
background: var(--left-pane-item-selected-background) !important; background: var(--left-pane-item-selected-background) !important;
box-shadow: var(--left-pane-item-selected-shadow); box-shadow: var(--left-pane-item-selected-shadow);
border-radius: 6px; border-radius: 6px;
@@ -676,7 +676,7 @@ body.layout-vertical.background-effects div.quick-search .dropdown-menu {
#left-pane span.fancytree-node.protected > span.fancytree-custom-icon:after { #left-pane span.fancytree-node.protected > span.fancytree-custom-icon:after {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
right: 0; inset-inline-end: 0;
font-size: 14px; font-size: 14px;
content: "\eb4a"; content: "\eb4a";
font-family: "boxicons"; font-family: "boxicons";
@@ -685,6 +685,10 @@ body.layout-vertical.background-effects div.quick-search .dropdown-menu {
border-radius: 50%; border-radius: 50%;
} }
body[dir=rtl] #left-pane span.fancytree-node.protected > span.fancytree-custom-icon:after {
transform: translateX(-25%);
}
body.mobile .fancytree-expander::before, body.mobile .fancytree-expander::before,
body.mobile .fancytree-title, body.mobile .fancytree-title,
body.mobile .fancytree-node > span { body.mobile .fancytree-node > span {
@@ -699,7 +703,7 @@ body.mobile .fancytree-node > span {
body.mobile:not(.force-fixed-tree) #mobile-sidebar-wrapper { body.mobile:not(.force-fixed-tree) #mobile-sidebar-wrapper {
border-top-right-radius: 12px; border-top-right-radius: 12px;
border-bottom-right-radius: 12px; border-bottom-right-radius: 12px;
border-right: 1px solid var(--subtle-border-color); border-inline-end: 1px solid var(--subtle-border-color);
} }
} }
@@ -734,7 +738,7 @@ body.mobile .fancytree-node > span {
} }
#left-pane .tree-item-button { #left-pane .tree-item-button {
margin-right: 6px; margin-inline-end: 6px;
border: unset; border: unset;
border-radius: 50%; border-radius: 50%;
background: var(--left-pane-item-action-button-background); background: var(--left-pane-item-action-button-background);
@@ -768,12 +772,12 @@ body.mobile .fancytree-node > span {
/* Toolbar container (collapsed state) */ /* Toolbar container (collapsed state) */
#left-pane .tree-actions { #left-pane .tree-actions {
max-width: var(--tree-actions-toolbar-collapsed-width); max-width: var(--tree-actions-toolbar-collapsed-width);
right: var(--tree-actions-toolbar-horizontal-margin); inset-inline-end: var(--tree-actions-toolbar-horizontal-margin);
bottom: var(--tree-actions-toolbar-vertical-margin); bottom: var(--tree-actions-toolbar-vertical-margin);
overflow: hidden; overflow: hidden;
border: 1px solid transparent; border: 1px solid transparent;
padding: var(--tree-actions-toolbar-padding-size); padding: var(--tree-actions-toolbar-padding-size);
padding-right: var(--tree-actions-toolbar-collapsed-width); padding-inline-end: var(--tree-actions-toolbar-collapsed-width);
background: transparent; background: transparent;
transition: transition:
max-width 400ms ease-out, max-width 400ms ease-out,
@@ -817,7 +821,7 @@ body.mobile .fancytree-node > span {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
top: 50%; top: 50%;
right: calc((var(--tree-actions-toolbar-collapsed-width) - var(--tree-actions-toolbar-expand-button-size)) / 2); inset-inline-end: calc((var(--tree-actions-toolbar-collapsed-width) - var(--tree-actions-toolbar-expand-button-size)) / 2);
width: var(--tree-actions-toolbar-expand-button-size); width: var(--tree-actions-toolbar-expand-button-size);
height: var(--tree-actions-toolbar-expand-button-size); height: var(--tree-actions-toolbar-expand-button-size);
box-shadow: 2px 2px 6px var(--left-pane-background-color); box-shadow: 2px 2px 6px var(--left-pane-background-color);
@@ -906,8 +910,8 @@ body.electron.background-effects.layout-horizontal .tab-row-container .toggle-bu
content: ""; content: "";
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: -10px; inset-inline-start: -10px;
right: -10px; inset-inline-end: -10px;
top: 32px; top: 32px;
height: 1px; height: 1px;
border-bottom: 1px solid var(--launcher-pane-horiz-border-color); border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
@@ -918,13 +922,13 @@ body.electron.background-effects.layout-horizontal .tab-row-container .tab-scrol
position: relative; position: relative;
} }
body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-left:after, body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-inset-inline-start:after,
body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-right:after { body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-inset-inline-end:after {
content: ""; content: "";
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0px; inset-inline-start: 0px;
right: 0px; inset-inline-end: 0px;
height: 1px; height: 1px;
border-bottom: 1px solid var(--launcher-pane-horiz-border-color); border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
} }
@@ -933,9 +937,9 @@ body.electron.background-effects.layout-horizontal .tab-row-container .note-tab[
content: ""; content: "";
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: -32768px; inset-inline-start: -32768px;
top: var(--tab-height); top: var(--tab-height);
right: calc(100% - 1px); inset-inline-end: calc(100% - 1px);
height: 1px; height: 1px;
border-bottom: 1px solid var(--launcher-pane-horiz-border-color); border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
} }
@@ -944,9 +948,9 @@ body.electron.background-effects.layout-horizontal .tab-row-container .note-tab[
content: ""; content: "";
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 100%; inset-inline-start: 100%;
top: var(--tab-height); top: var(--tab-height);
right: 0; inset-inline-end: 0;
width: 100vw; width: 100vw;
height: 1px; height: 1px;
border-bottom: 1px solid var(--launcher-pane-horiz-border-color); border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
@@ -956,9 +960,9 @@ body.electron.background-effects.layout-horizontal .tab-row-container .note-new-
content: ""; content: "";
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: -4px; inset-inline-start: -4px;
top: calc(var(--tab-height), -1); top: calc(var(--tab-height), -1);
right: 0; inset-inline-end: 0;
width: 100vw; width: 100vw;
height: 1px; height: 1px;
border-bottom: 1px solid var(--launcher-pane-horiz-border-color); border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
@@ -1045,18 +1049,18 @@ body.layout-horizontal .tab-row-widget .note-tab .note-tab-wrapper {
content: ""; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
height: 3px; height: 3px;
background-color: var(--workspace-tab-background-color); background-color: var(--workspace-tab-background-color);
} }
.tab-row-widget .note-tab:nth-child(1) { body:not([dir=rtl]) .tab-row-widget .note-tab:nth-child(1) {
transform: translate3d(var(--tab-first-item-horiz-offset), 0, 0); transform: translate3d(var(--tab-first-item-horiz-offset), 0, 0);
} }
:root .tab-row-widget .note-tab .note-tab-icon { :root .tab-row-widget .note-tab .note-tab-icon {
padding-right: 5px; /* The gap between the icon and the title */ padding-inline-end: 5px; /* The gap between the icon and the title */
} }
.tab-row-widget .note-tab[active] .note-tab-icon { .tab-row-widget .note-tab[active] .note-tab-icon {
@@ -1101,7 +1105,7 @@ body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .
.tab-row-widget .note-new-tab { .tab-row-widget .note-new-tab {
position: relative; position: relative;
margin-left: 3px; margin-inline-start: 3px;
color: transparent; /* Prevent the original "+" from being displayed */ color: transparent; /* Prevent the original "+" from being displayed */
} }
@@ -1114,7 +1118,7 @@ body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .
position: absolute; position: absolute;
content: ""; content: "";
top: calc((var(--tab-height) - var(--new-tab-button-size)) / 2); top: calc((var(--tab-height) - var(--new-tab-button-size)) / 2);
left: calc((var(--tab-height) - var(--new-tab-button-size)) / 2); inset-inline-start: calc((var(--tab-height) - var(--new-tab-button-size)) / 2);
width: var(--new-tab-button-size); width: var(--new-tab-button-size);
height: var(--new-tab-button-size); height: var(--new-tab-button-size);
background: var(--new-tab-button-background); background: var(--new-tab-button-background);
@@ -1139,7 +1143,7 @@ body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .
display: flex; display: flex;
position: absolute; position: absolute;
content: "\ebc0"; content: "\ebc0";
left: 0; inset-inline-start: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -1221,23 +1225,23 @@ body.mobile .note-title {
} }
.title-row > *:first-child { .title-row > *:first-child {
margin-right: 0; margin-inline-end: 0;
} }
.title-row > *:nth-child(2) { .title-row > *:nth-child(2) {
margin-left: 0; margin-inline-start: 0;
} }
.title-row { .title-row {
/* Aligns the "Create new split" button with the note menu button (the three dots button) */ /* Aligns the "Create new split" button with the note menu button (the three dots button) */
padding-right: 3px; padding-inline-end: 3px;
} }
.note-title-widget input { .note-title-widget input {
--input-background-color: transparent; --input-background-color: transparent;
border-radius: 8px; border-radius: 8px;
padding-left: 12px; padding-inline-start: 12px;
} }
/* The "Change note icon" button */ /* The "Change note icon" button */
@@ -1312,7 +1316,7 @@ body.mobile .note-title {
/* The promoted attributes section */ /* The promoted attributes section */
div.promoted-attributes-container { div.promoted-attributes-container {
display: flex; display: flex;
margin-right: 10%; margin-inline-end: 10%;
padding: 6px 0; padding: 6px 0;
gap: 8px; gap: 8px;
align-items: stretch; align-items: stretch;
@@ -1326,8 +1330,8 @@ div.promoted-attributes-container input {
/* A promoted attribute card */ /* A promoted attribute card */
div.promoted-attribute-cell { div.promoted-attribute-cell {
--pa-card-padding-left: 16px; --pa-card-padding-inline-start: 16px;
--pa-card-padding-right: 2px; --pa-card-padding-inline-end: 2px;
--input-background-color: transparent; --input-background-color: transparent;
box-shadow: 1px 1px 2px var(--promoted-attribute-card-shadow-color); box-shadow: 1px 1px 2px var(--promoted-attribute-card-shadow-color);
@@ -1335,7 +1339,7 @@ div.promoted-attribute-cell {
display: inline-flex; display: inline-flex;
margin: 0; margin: 0;
border-radius: 8px; border-radius: 8px;
padding: 2px var(--pa-card-padding-right) 2px var(--pa-card-padding-left); padding: 2px var(--pa-card-padding-inline-end) 2px var(--pa-card-padding-inline-start);
background: var(--promoted-attribute-card-background-color); background: var(--promoted-attribute-card-background-color);
overflow-y: visible; overflow-y: visible;
} }
@@ -1350,7 +1354,7 @@ div.promoted-attribute-cell {
/* A promoted attribute card (boolean attribute) */ /* A promoted attribute card (boolean attribute) */
div.promoted-attribute-cell:has(input[type="checkbox"]):not(:has(.multiplicity > span)) { div.promoted-attribute-cell:has(input[type="checkbox"]):not(:has(.multiplicity > span)) {
/* Checbox attribute, without multiplicity */ /* Checbox attribute, without multiplicity */
padding-right: var(--pa-card-padding-left); padding-inline-end: var(--pa-card-padding-inline-start);
} }
div.promoted-attribute-cell > * { div.promoted-attribute-cell > * {
@@ -1400,15 +1404,15 @@ div.promoted-attribute-cell .tn-checkbox {
/* Relocate the checkbox before the label */ /* Relocate the checkbox before the label */
div.promoted-attribute-cell.promoted-attribute-label-boolean > div:first-of-type { div.promoted-attribute-cell.promoted-attribute-label-boolean > div:first-of-type {
order: -1; order: -1;
margin-right: 1.5em; margin-inline-end: 1.5em;
} }
/* The element containing the "new attribute" and "remove this attribute button" */ /* The element containing the "new attribute" and "remove this attribute button" */
div.promoted-attribute-cell .multiplicity:has(span) { div.promoted-attribute-cell .multiplicity:has(span) {
--icon-button-size: 24px; --icon-button-size: 24px;
margin-left: 8px; margin-inline-start: 8px;
margin-right: calc(var(--pa-card-padding-left) - var(--pa-card-padding-right)); margin-inline-end: calc(var(--pa-card-padding-inline-start) - var(--pa-card-padding-inline-end));
font-size: 0; /* Prevent whitespaces creating a gap between buttons */ font-size: 0; /* Prevent whitespaces creating a gap between buttons */
display: flex; display: flex;
} }
@@ -1436,6 +1440,10 @@ div#center-pane .floating-buttons-children {
opacity 250ms ease-out; opacity 250ms ease-out;
} }
body[dir=rtl] div#center-pane .floating-buttons-children {
transform-origin: left;
}
/* Floating buttons container (collapsed) */ /* Floating buttons container (collapsed) */
div#center-pane .floating-buttons-children.temporarily-hidden { div#center-pane .floating-buttons-children.temporarily-hidden {
display: flex !important; display: flex !important;
@@ -1547,7 +1555,7 @@ div.floating-buttons-children .close-floating-buttons {
} }
div.floating-buttons-children .close-floating-buttons { div.floating-buttons-children .close-floating-buttons {
margin-left: 0 !important; margin-inline-start: 0 !important;
background: var(--floating-button-hide-button-background); background: var(--floating-button-hide-button-background);
color: var(--floating-button-hide-button-color); color: var(--floating-button-hide-button-color);
} }
@@ -1637,12 +1645,12 @@ div.find-replace-widget div.find-widget-found-wrapper > span {
} }
.find-replace-widget .form-check { .find-replace-widget .form-check {
padding-left: 0; padding-inline-start: 0;
white-space: nowrap; white-space: nowrap;
} }
.find-replace-widget .form-check .form-check-input { .find-replace-widget .form-check .form-check-input {
margin-left: 0; margin-inline-start: 0;
} }
/* Narrow version */ /* Narrow version */
@@ -1662,13 +1670,13 @@ div.find-replace-widget div.find-widget-found-wrapper > span {
.find-widget-box, .find-widget-box,
.replace-widget-box { .replace-widget-box {
padding-right: 3em !important; padding-inline-end: 3em !important;
} }
.find-widget-close-button { .find-widget-close-button {
position: absolute; position: absolute;
top: .85em; top: .85em;
right: .5em; inset-inline-end: .5em;
} }
.find-widget-box > * { .find-widget-box > * {
@@ -1700,7 +1708,7 @@ div.find-replace-widget div.find-widget-found-wrapper > span {
} }
.replace-widget-box > * { .replace-widget-box > * {
margin-right: unset !important; margin-inline-end: unset !important;
} }
div.replace-widget-box button.btn.btn-sm { div.replace-widget-box button.btn.btn-sm {
@@ -1743,7 +1751,7 @@ div.find-replace-widget div.find-widget-found-wrapper > span {
#right-pane .toc li, #right-pane .toc li,
#right-pane .highlights-list li { #right-pane .highlights-list li {
padding-top: 2px; padding-top: 2px;
padding-right: 8px; padding-inline-end: 8px;
padding-bottom: 2px; padding-bottom: 2px;
border-radius: 4px; border-radius: 4px;
text-align: unset; text-align: unset;
@@ -1806,8 +1814,8 @@ div.find-replace-widget div.find-widget-found-wrapper > span {
} }
.excalidraw .dropdown-menu .dropdown-menu-container > div:not([class]):not(:last-child) { .excalidraw .dropdown-menu .dropdown-menu-container > div:not([class]):not(:last-child) {
margin-left: calc(var(--padding) * var(--space-factor) * -1) !important; margin-inline-start: calc(var(--padding) * var(--space-factor) * -1) !important;
margin-right: calc(var(--padding) * var(--space-factor) * -1) !important; margin-inline-end: calc(var(--padding) * var(--space-factor) * -1) !important;
} }
.excalidraw .dropdown-menu:before { .excalidraw .dropdown-menu:before {

View File

@@ -1,5 +1,5 @@
ul.fancytree-container { ul.fancytree-container {
padding-left: 0; padding-inline-start: 0;
} }
ul.fancytree-container li { ul.fancytree-container li {
@@ -15,7 +15,7 @@ span.fancytree-node.fancytree-hide {
flex-shrink: 1; flex-shrink: 1;
flex-grow: 1; flex-grow: 1;
overflow: hidden; overflow: hidden;
margin-left: 7px; margin-inline-start: 7px;
outline: none; outline: none;
position: relative; position: relative;
top: 2px; top: 2px;
@@ -59,7 +59,11 @@ span.fancytree-node.fancytree-hide {
line-height: 1; line-height: 1;
position: relative; position: relative;
top: 2px; top: 2px;
margin-right: 5px; margin-inline-end: 5px;
}
body[dir=rtl] .fancytree-node:not(.fancytree-loading):not(.fancytree-expanded) .fancytree-expander:before {
content: "\ea4d"; /* bx bx-chevron-left */
} }
.fancytree-loading span.fancytree-expander { .fancytree-loading span.fancytree-expander {
@@ -80,7 +84,7 @@ span.fancytree-node.fancytree-hide {
width: 12px; width: 12px;
height: 12px; height: 12px;
margin-top: 2px; margin-top: 2px;
margin-left: 1px; margin-inline-start: 1px;
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
} }
@@ -169,11 +173,11 @@ span.fancytree-node.fancytree-active-clone:not(.fancytree-active) .fancytree-tit
/* first nesting level has lower left padding to avoid extra left padding. Other levels are not affected */ /* first nesting level has lower left padding to avoid extra left padding. Other levels are not affected */
.ui-fancytree > li > ul { .ui-fancytree > li > ul {
padding-left: 5px; padding-inline-start: 5px;
} }
.ui-fancytree ul { .ui-fancytree ul {
padding-left: 20px; padding-inline-start: 20px;
} }
span.fancytree-active { span.fancytree-active {
@@ -229,14 +233,14 @@ span.fancytree-node.archived {
display: none; display: none;
font-size: 120%; font-size: 120%;
cursor: pointer; cursor: pointer;
margin-left: 8px; margin-inline-start: 8px;
padding: 1px; padding: 1px;
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 5px; border-radius: 5px;
} }
.unhoist-button.bx.tree-item-button { .unhoist-button.bx.tree-item-button {
margin-left: 0; /* unhoist button is on the left and doesn't need more margin */ margin-inline-start: 0; /* unhoist button is on the left and doesn't need more margin */
display: block; /* keep always visible */ display: block; /* keep always visible */
} }

View File

@@ -736,6 +736,7 @@
"mobile_detail_menu": { "mobile_detail_menu": {
"insert_child_note": "Insert child note", "insert_child_note": "Insert child note",
"delete_this_note": "Delete this note", "delete_this_note": "Delete this note",
"note_revisions": "Note revisions",
"error_cannot_get_branch_id": "Cannot get branchId for notePath '{{notePath}}'", "error_cannot_get_branch_id": "Cannot get branchId for notePath '{{notePath}}'",
"error_unrecognized_command": "Unrecognized command {{command}}" "error_unrecognized_command": "Unrecognized command {{command}}"
}, },
@@ -1423,7 +1424,8 @@
"min-days-in-first-week": "Minimum days in first week", "min-days-in-first-week": "Minimum days in first week",
"first-week-info": "First week contains first Thursday of the year is based on <a href=\"https://en.wikipedia.org/wiki/ISO_week_date#First_week\">ISO 8601</a> standard.", "first-week-info": "First week contains first Thursday of the year is based on <a href=\"https://en.wikipedia.org/wiki/ISO_week_date#First_week\">ISO 8601</a> standard.",
"first-week-warning": "Changing first week options may cause duplicate with existing Week Notes and the existing Week Notes will not be updated accordingly.", "first-week-warning": "Changing first week options may cause duplicate with existing Week Notes and the existing Week Notes will not be updated accordingly.",
"formatting-locale": "Date & number format" "formatting-locale": "Date & number format",
"formatting-locale-auto": "Based on the application's language"
}, },
"backup": { "backup": {
"automatic_backup": "Automatic backup", "automatic_backup": "Automatic backup",

View File

@@ -46,6 +46,7 @@ interface CustomGlobals {
platform?: typeof process.platform; platform?: typeof process.platform;
linter: typeof lint; linter: typeof lint;
hasNativeTitleBar: boolean; hasNativeTitleBar: boolean;
isRtl: boolean;
} }
type RequireMethod = (moduleName: string) => any; type RequireMethod = (moduleName: string) => any;

View File

@@ -1,3 +1,5 @@
import options from "../services/options";
type DateTimeStyle = "full" | "long" | "medium" | "short" | "none" | undefined; type DateTimeStyle = "full" | "long" | "medium" | "short" | "none" | undefined;
/** /**
@@ -8,7 +10,7 @@ export function formatDateTime(date: string | Date | number | null | undefined,
return ""; return "";
} }
const locale = navigator.language; const locale = options.get("formattingLocale") || options.get("locale") || navigator.language;
let parsedDate; let parsedDate;
if (typeof date === "string" || typeof date === "number") { if (typeof date === "string" || typeof date === "number") {
@@ -24,7 +26,7 @@ export function formatDateTime(date: string | Date | number | null | undefined,
if (timeStyle !== "none" && dateStyle !== "none") { if (timeStyle !== "none" && dateStyle !== "none") {
// Format the date and time // Format the date and time
const formatter = new Intl.DateTimeFormat(navigator.language, { dateStyle, timeStyle }); const formatter = new Intl.DateTimeFormat(locale, { dateStyle, timeStyle });
return formatter.format(parsedDate); return formatter.format(parsedDate);
} else if (timeStyle === "none" && dateStyle !== "none") { } else if (timeStyle === "none" && dateStyle !== "none") {
// Format only the date // Format only the date

View File

@@ -7,7 +7,7 @@
.show-floating-buttons { .show-floating-buttons {
position: absolute; position: absolute;
top: var(--floating-buttons-vert-offset, 10px); top: var(--floating-buttons-vert-offset, 10px);
right: var(--floating-buttons-horiz-offset, 10px); inset-inline-end: var(--floating-buttons-horiz-offset, 10px);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
z-index: 100; z-index: 100;
@@ -15,8 +15,7 @@
.note-split.rtl .floating-buttons-children, .note-split.rtl .floating-buttons-children,
.note-split.rtl .show-floating-buttons { .note-split.rtl .show-floating-buttons {
right: unset; inset-inline-end: 10px;
left: 10px;
} }
.note-split.rtl .close-floating-buttons { .note-split.rtl .close-floating-buttons {
@@ -74,7 +73,7 @@
.show-floating-buttons { .show-floating-buttons {
/* display: none;*/ /* display: none;*/
margin-left: 5px !important; margin-inline-start: 5px !important;
} }
.show-floating-buttons-button { .show-floating-buttons-button {
@@ -97,7 +96,7 @@
/* #region Close floating buttons */ /* #region Close floating buttons */
.close-floating-buttons { .close-floating-buttons {
margin-left: 5px !important; margin-inline-start: 5px !important;
} }
.close-floating-buttons:first-child { .close-floating-buttons:first-child {
@@ -140,7 +139,7 @@
z-index: 10; z-index: 10;
position: absolute; position: absolute;
top: 50px; top: 50px;
right: 10px; inset-inline-end: 10px;
width: 400px; width: 400px;
border-radius: 10px; border-radius: 10px;
background-color: var(--accented-background-color); background-color: var(--accented-background-color);
@@ -150,8 +149,8 @@
} }
.backlink-excerpt { .backlink-excerpt {
border-left: 2px solid var(--main-border-color); border-inline-start: 2px solid var(--main-border-color);
padding-left: 10px; padding-inline-start: 10px;
opacity: 80%; opacity: 80%;
font-size: 90%; font-size: 90%;
} }

View File

@@ -23,6 +23,6 @@
color: var(--button-text-color); color: var(--button-text-color);
position: absolute; position: absolute;
top: 10px; top: 10px;
right: 10px; inset-inline-end: 10px;
cursor: pointer; cursor: pointer;
} }

View File

@@ -30,7 +30,7 @@ const TPL = /*html*/`
} }
.attachment-details { .attachment-details {
margin-left: 10px; margin-inline-start: 10px;
} }
.attachment-content-wrapper { .attachment-content-wrapper {

View File

@@ -36,7 +36,7 @@ const TPL = /*html*/`
} }
.related-notes-list { .related-notes-list {
padding-left: 20px; padding-inline-start: 20px;
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
} }
@@ -46,7 +46,7 @@ const TPL = /*html*/`
} }
.attr-edit-table th { .attr-edit-table th {
text-align: left; text-align: start;
} }
.attr-edit-table td input[not(type="checkbox")] { .attr-edit-table td input[not(type="checkbox")] {
@@ -150,7 +150,7 @@ const TPL = /*html*/`
<th title="${t("attribute_detail.precision_title")}">${t("attribute_detail.precision")}</th> <th title="${t("attribute_detail.precision_title")}">${t("attribute_detail.precision")}</th>
<td> <td>
<div class="input-group"> <div class="input-group">
<input type="number" class="form-control attr-input-number-precision" style="text-align: right"> <input type="number" class="form-control attr-input-number-precision" style="text-align: end">
<span class="input-group-text">${t("attribute_detail.digits")}</span> <span class="input-group-text">${t("attribute_detail.digits")}</span>
</div> </div>
</td> </td>
@@ -176,7 +176,7 @@ const TPL = /*html*/`
<div class="attr-save-delete-button-container"> <div class="attr-save-delete-button-container">
<button class="btn btn-primary btn-sm attr-save-changes-and-close-button" <button class="btn btn-primary btn-sm attr-save-changes-and-close-button"
style="flex-grow: 1; margin-right: 20px"> style="flex-grow: 1; margin-inline-end: 20px">
${t("attribute_detail.save_and_close")}</button> ${t("attribute_detail.save_and_close")}</button>
<button class="btn btn-secondary btn-sm attr-delete-button"> <button class="btn btn-secondary btn-sm attr-delete-button">

View File

@@ -76,7 +76,7 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
/** /**
* Sets the CSS attribute of the given name to the given value. * Sets the CSS attribute of the given name to the given value.
* *
* @param name the name of the CSS attribute to set (e.g. `padding-left`). * @param name the name of the CSS attribute to set (e.g. `padding-inline-start`).
* @param value the value of the CSS attribute to set (e.g. `12px`). * @param value the value of the CSS attribute to set (e.g. `12px`).
* @returns self for chaining. * @returns self for chaining.
*/ */
@@ -89,7 +89,7 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
* Sets the CSS attribute of the given name to the given value, but only if the condition provided is truthy. * Sets the CSS attribute of the given name to the given value, but only if the condition provided is truthy.
* *
* @param condition `true` in order to apply the CSS, `false` to ignore it. * @param condition `true` in order to apply the CSS, `false` to ignore it.
* @param name the name of the CSS attribute to set (e.g. `padding-left`). * @param name the name of the CSS attribute to set (e.g. `padding-inline-start`).
* @param value the value of the CSS attribute to set (e.g. `12px`). * @param value the value of the CSS attribute to set (e.g. `12px`).
* @returns self for chaining. * @returns self for chaining.
*/ */

View File

@@ -4,7 +4,7 @@ import AbstractBulkAction from "./abstract_bulk_action";
import HelpRemoveButtons from "../react/HelpRemoveButtons"; import HelpRemoveButtons from "../react/HelpRemoveButtons";
interface BulkActionProps { interface BulkActionProps {
label: string | ComponentChildren; label: string | ComponentChildren;
children?: ComponentChildren; children?: ComponentChildren;
helpText?: ComponentChildren; helpText?: ComponentChildren;
bulkAction: AbstractBulkAction; bulkAction: AbstractBulkAction;
@@ -12,8 +12,8 @@ interface BulkActionProps {
// Define styles as constants to prevent recreation // Define styles as constants to prevent recreation
const flexContainerStyle = { display: "flex", alignItems: "center" } as const; const flexContainerStyle = { display: "flex", alignItems: "center" } as const;
const labelStyle = { marginRight: "10px" } as const; const labelStyle = { marginInlineEnd: "10px" } as const;
const textStyle = { marginRight: "10px", marginLeft: "10px" } as const; const textStyle = { marginInlineEnd: "10px", marginInlineStart: "10px" } as const;
const BulkAction = memo(({ label, children, helpText, bulkAction }: BulkActionProps) => { const BulkAction = memo(({ label, children, helpText, bulkAction }: BulkActionProps) => {
return ( return (
@@ -44,4 +44,4 @@ export const BulkActionText = memo(({ text }: { text: string }) => {
{text} {text}
</div> </div>
); );
}); });

View File

@@ -1,5 +1,6 @@
import { Tooltip } from "bootstrap"; import { Tooltip } from "bootstrap";
import NoteContextAwareWidget from "../note_context_aware_widget.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js";
import { handleRightToLeftPlacement } from "../../services/utils.js";
const TPL = /*html*/`<button class="button-widget bx" const TPL = /*html*/`<button class="button-widget bx"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
@@ -26,13 +27,14 @@ export default class AbstractButtonWidget<SettingsT extends AbstractButtonWidget
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.tooltip = new Tooltip(this.$widget[0], { this.tooltip = new Tooltip(this.$widget[0], {
html: true, html: true,
// in case getTitle() returns null -> use empty string as fallback // in case getTitle() returns null -> use empty string as fallback
title: () => this.getTitle() || "", title: () => this.getTitle() || "",
trigger: "hover", trigger: "hover",
placement: this.settings.titlePlacement, placement: handleRightToLeftPlacement(this.settings.titlePlacement),
fallbackPlacements: [this.settings.titlePlacement] fallbackPlacements: [ handleRightToLeftPlacement(this.settings.titlePlacement) ]
}); });
if (this.settings.onContextMenu) { if (this.settings.onContextMenu) {

View File

@@ -28,7 +28,7 @@ const TPL = /*html*/`
position: relative; position: relative;
top: 3px; top: 3px;
font-size: 120%; font-size: 120%;
margin-right: 5px; margin-inline-end: 5px;
} }
.attachment-actions .dropdown-item[disabled], .attachment-actions .dropdown-item[disabled]:hover { .attachment-actions .dropdown-item[disabled], .attachment-actions .dropdown-item[disabled]:hover {

View File

@@ -35,7 +35,7 @@ const DROPDOWN_TPL = `
} }
.bookmark-folder-widget li .note-link { .bookmark-folder-widget li .note-link {
padding-left: 35px; padding-inline-start: 35px;
} }
</style> </style>

View File

@@ -55,7 +55,7 @@ button.global-menu-button {
.global-menu-button-update-available { .global-menu-button-update-available {
position: absolute; position: absolute;
right: -30px; inset-inline-end: -30px;
bottom: -30px; bottom: -30px;
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -69,7 +69,7 @@ button.global-menu-button {
} }
.global-menu .zoom-buttons { .global-menu .zoom-buttons {
margin-left: 2em; margin-inline-start: 2em;
} }
.global-menu .zoom-buttons a { .global-menu .zoom-buttons a {
@@ -79,7 +79,7 @@ button.global-menu-button {
color: var(--button-text-color); color: var(--button-text-color);
background-color: var(--button-background-color); background-color: var(--button-background-color);
padding: 3px; padding: 3px;
margin-left: 3px; margin-inline-start: 3px;
text-decoration: none; text-decoration: none;
} }
@@ -88,15 +88,15 @@ button.global-menu-button {
} }
.global-menu .zoom-state { .global-menu .zoom-state {
margin-left: 5px; margin-inline-start: 5px;
margin-right: 5px; margin-inline-end: 5px;
} }
.global-menu .dropdown-item .bx { .global-menu .dropdown-item .bx {
position: relative; position: relative;
top: 3px; top: 3px;
font-size: 120%; font-size: 120%;
margin-right: 6px; margin-inline-end: 6px;
} }
/* #region Update available */ /* #region Update available */

View File

@@ -1,3 +1,4 @@
import { handleRightToLeftPlacement } from "../../services/utils.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import { Tooltip, Dropdown } from "bootstrap"; import { Tooltip, Dropdown } from "bootstrap";
type PopoverPlacement = Tooltip.PopoverPlacement; type PopoverPlacement = Tooltip.PopoverPlacement;
@@ -48,8 +49,8 @@ export default class RightDropdownButtonWidget extends BasicWidget {
this.$tooltip = this.$widget.find(".tooltip-trigger").attr("title", this.title); this.$tooltip = this.$widget.find(".tooltip-trigger").attr("title", this.title);
this.tooltip = new Tooltip(this.$tooltip[0], { this.tooltip = new Tooltip(this.$tooltip[0], {
placement: this.settings.titlePlacement, placement: handleRightToLeftPlacement(this.settings.titlePlacement),
fallbackPlacements: [this.settings.titlePlacement] fallbackPlacements: [ handleRightToLeftPlacement(this.settings.titlePlacement) ]
}); });
this.$widget this.$widget

View File

@@ -11,7 +11,7 @@ body.zen .close-zen-container {
display: block; display: block;
position: fixed; position: fixed;
top: 2px; top: 2px;
right: 2px; inset-inline-end: 2px;
z-index: 9999; z-index: 9999;
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
} }
@@ -21,6 +21,6 @@ body.zen.mobile .close-zen-container {
} }
body.zen.electron:not(.platform-darwin):not(.native-titlebar) .close-zen-container { body.zen.electron:not(.platform-darwin):not(.native-titlebar) .close-zen-container {
left: calc(env(titlebar-area-width) - var(--zen-button-size) - 2px); inset-inline-start: calc(env(titlebar-area-width) - var(--zen-button-size) - 2px);
right: unset; inset-inline-end: unset;
} }

View File

@@ -93,7 +93,7 @@
.board-view-container .board-column .edit-icon { .board-view-container .board-column .edit-icon {
opacity: 0; opacity: 0;
margin-left: 0.5em; margin-inline-start: 0.5em;
transition: opacity 0.2s ease; transition: opacity 0.2s ease;
color: var(--muted-text-color); color: var(--muted-text-color);
cursor: pointer; cursor: pointer;
@@ -146,14 +146,14 @@
} }
.board-view-container .board-note .icon { .board-view-container .board-note .icon {
margin-right: 0.25em; margin-inline-end: 0.25em;
display: inline; display: inline;
} }
.board-view-container .board-note > .edit-icon { .board-view-container .board-note > .edit-icon {
position: absolute; position: absolute;
top: 8px; top: 8px;
right: 4px; inset-inline-end: 4px;
padding: 2px; padding: 2px;
background-color: var(--main-background-color); background-color: var(--main-background-color);
} }
@@ -165,10 +165,10 @@
.board-view-container .board-note:hover > .edit-icon { .board-view-container .board-note:hover > .edit-icon {
position: absolute; position: absolute;
top: 8px; top: 8px;
right: 4px; inset-inline-end: 4px;
color: var(--main-text-color); color: var(--main-text-color);
background-color: var(--main-background-color); background-color: var(--main-background-color);
padding-left: 6px; padding-inline-start: 6px;
} }
.board-view-container .board-note.fade-in { .board-view-container .board-note.fade-in {
@@ -269,7 +269,7 @@
} }
.board-new-item .icon { .board-new-item .icon {
margin-right: 0.25em; margin-inline-end: 0.25em;
} }
.board-add-column { .board-add-column {
@@ -296,7 +296,7 @@
} }
.board-add-column .icon { .board-add-column .icon {
margin-right: 0.5em; margin-inline-end: 0.5em;
font-size: 1.2em; font-size: 1.2em;
} }

View File

@@ -13,8 +13,8 @@
.search-result-widget-content .calendar-view { .search-result-widget-content .calendar-view {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
bottom: 0; bottom: 0;
} }
@@ -67,10 +67,10 @@
} }
body.desktop:not(.zen) .calendar-view .calendar-header { body.desktop:not(.zen) .calendar-view .calendar-header {
padding-right: 5em; padding-inline-end: 5em;
} }
.search-result-widget-content .calendar-view .calendar-header { .search-result-widget-content .calendar-view .calendar-header {
padding-right: unset !important; padding-inline-end: unset !important;
} }
/* #endregion */ /* #endregion */

View File

@@ -3,8 +3,8 @@ import { ViewModeProps } from "../interface";
import Calendar from "./calendar"; import Calendar from "./calendar";
import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks"; import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks";
import "./index.css"; import "./index.css";
import { useNoteLabel, useNoteLabelBoolean, useResizeObserver, useSpacedUpdate, useTouchBar, useTriliumEvent, useTriliumOption, useTriliumOptionInt } from "../../react/hooks"; import { useNoteLabel, useNoteLabelBoolean, useResizeObserver, useSpacedUpdate, useTriliumEvent, useTriliumOption, useTriliumOptionInt } from "../../react/hooks";
import { LOCALE_IDS } from "@triliumnext/commons"; import { DISPLAYABLE_LOCALE_IDS, LOCALE_IDS } from "@triliumnext/commons";
import { Calendar as FullCalendar } from "@fullcalendar/core"; import { Calendar as FullCalendar } from "@fullcalendar/core";
import { parseStartEndDateFromEvent, parseStartEndTimeFromEvent } from "./utils"; import { parseStartEndDateFromEvent, parseStartEndTimeFromEvent } from "./utils";
import dialog from "../../../services/dialog"; import dialog from "../../../services/dialog";
@@ -62,7 +62,7 @@ const CALENDAR_VIEWS = [
const SUPPORTED_CALENDAR_VIEW_TYPE = CALENDAR_VIEWS.map(v => v.type); const SUPPORTED_CALENDAR_VIEW_TYPE = CALENDAR_VIEWS.map(v => v.type);
// Here we hard-code the imports in order to ensure that they are embedded by webpack without having to load all the languages. // Here we hard-code the imports in order to ensure that they are embedded by webpack without having to load all the languages.
export const LOCALE_MAPPINGS: Record<LOCALE_IDS, (() => Promise<{ default: LocaleInput }>) | null> = { export const LOCALE_MAPPINGS: Record<DISPLAYABLE_LOCALE_IDS, (() => Promise<{ default: LocaleInput }>) | null> = {
de: () => import("@fullcalendar/core/locales/de"), de: () => import("@fullcalendar/core/locales/de"),
es: () => import("@fullcalendar/core/locales/es"), es: () => import("@fullcalendar/core/locales/es"),
fr: () => import("@fullcalendar/core/locales/fr"), fr: () => import("@fullcalendar/core/locales/fr"),
@@ -74,7 +74,9 @@ export const LOCALE_MAPPINGS: Record<LOCALE_IDS, (() => Promise<{ default: Local
pt: () => import("@fullcalendar/core/locales/pt"), pt: () => import("@fullcalendar/core/locales/pt"),
"pt_br": () => import("@fullcalendar/core/locales/pt-br"), "pt_br": () => import("@fullcalendar/core/locales/pt-br"),
uk: () => import("@fullcalendar/core/locales/uk"), uk: () => import("@fullcalendar/core/locales/uk"),
en: null en: null,
"en_rtl": null,
ar: () => import("@fullcalendar/core/locales/ar")
}; };
export default function CalendarView({ note, noteIds }: ViewModeProps<CalendarViewData>) { export default function CalendarView({ note, noteIds }: ViewModeProps<CalendarViewData>) {

View File

@@ -36,14 +36,14 @@
.geo-map-container .leaflet-div-icon .icon-shadow { .geo-map-container .leaflet-div-icon .icon-shadow {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
z-index: -1; z-index: -1;
} }
.geo-map-container .leaflet-div-icon .bx { .geo-map-container .leaflet-div-icon .bx {
position: absolute; position: absolute;
top: 3px; top: 3px;
left: 2px; inset-inline-start: 2px;
background-color: white; background-color: white;
color: black; color: black;
padding: 2px; padding: 2px;
@@ -55,7 +55,7 @@
display: block; display: block;
position: absolute; position: absolute;
top: 100%; top: 100%;
left: 50%; inset-inline-start: 50%;
transform: translateX(-50%); transform: translateX(-50%);
font-size: 0.75rem; font-size: 0.75rem;
height: 1rem; height: 1rem;
@@ -68,6 +68,10 @@
overflow: hidden; overflow: hidden;
} }
body[dir=rtl] .geo-map-container .leaflet-div-icon .title-label {
transform: translateX(50%);
}
.geo-map-container .leaflet-div-icon .archived { .geo-map-container .leaflet-div-icon .archived {
opacity: 0.5; opacity: 0.5;
} }

View File

@@ -57,7 +57,7 @@
.note-book-header .note-icon { .note-book-header .note-icon {
font-size: 100%; font-size: 100%;
display: inline-block; display: inline-block;
padding-right: 7px; padding-inline-end: 7px;
position: relative; position: relative;
} }

View File

@@ -13,8 +13,8 @@
.search-result-widget-content .table-view { .search-result-widget-content .table-view {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
bottom: 0; bottom: 0;
} }
@@ -33,7 +33,7 @@
.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left, .tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left,
.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left { .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left {
border-right-width: 1px; border-inline-end-width: 1px;
} }
.tabulator .tabulator-row.archived { .tabulator .tabulator-row.archived {
@@ -65,7 +65,7 @@
.tabulator button.tree-collapse span { .tabulator button.tree-collapse span {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
font-size: 1.5em; font-size: 1.5em;
transform: translateY(-50%); transform: translateY(-50%);
} }

View File

@@ -3,6 +3,7 @@ import FlexContainer from "./flex_container.js";
import options from "../../services/options.js"; import options from "../../services/options.js";
import type BasicWidget from "../basic_widget.js"; import type BasicWidget from "../basic_widget.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import { LOCALES } from "@triliumnext/commons";
/** /**
* The root container is the top-most widget/container, from which the entire layout derives. * The root container is the top-most widget/container, from which the entire layout derives.
@@ -32,6 +33,7 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
this.#setMotion(options.is("motionEnabled")); this.#setMotion(options.is("motionEnabled"));
this.#setShadows(options.is("shadowsEnabled")); this.#setShadows(options.is("shadowsEnabled"));
this.#setBackdropEffects(options.is("backdropEffectsEnabled")); this.#setBackdropEffects(options.is("backdropEffectsEnabled"));
this.#setLocaleAndDirection(options.get("locale"));
return super.render(); return super.render();
} }
@@ -68,6 +70,12 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
#setBackdropEffects(enabled: boolean) { #setBackdropEffects(enabled: boolean) {
document.body.classList.toggle("backdrop-effects-disabled", !enabled); document.body.classList.toggle("backdrop-effects-disabled", !enabled);
} }
#setLocaleAndDirection(locale: string) {
const correspondingLocale = LOCALES.find(l => l.id === locale);
document.body.lang = locale;
document.body.dir = correspondingLocale?.rtl ? "rtl" : "ltr";
}
} }
function getViewportHeight() { function getViewportHeight() {

View File

@@ -4,7 +4,7 @@
.bulk-actions-dialog .bulk-available-action-list button { .bulk-actions-dialog .bulk-available-action-list button {
padding: 2px 7px; padding: 2px 7px;
margin-right: 10px; margin-inline-end: 10px;
margin-bottom: 5px; margin-bottom: 5px;
} }
@@ -19,7 +19,7 @@
.bulk-actions-dialog .bulk-existing-action-list .button-column { .bulk-actions-dialog .bulk-existing-action-list .button-column {
width: 50px; width: 50px;
white-space: nowrap; white-space: nowrap;
text-align: right; text-align: end;
} }
.bulk-actions-dialog .bulk-existing-action-list .button-column > * { .bulk-actions-dialog .bulk-existing-action-list .button-column > * {

View File

@@ -4,11 +4,11 @@
} }
.export-dialog form .format-choice { .export-dialog form .format-choice {
padding-left: 40px; padding-inline-start: 40px;
} }
.export-dialog form .opml-versions { .export-dialog form .opml-versions {
padding-left: 60px; padding-inline-start: 60px;
} }
.export-dialog form .form-check-label { .export-dialog form .form-check-label {

View File

@@ -46,8 +46,8 @@ const TPL = /*html*/`\
.modal.popup-editor-dialog .classic-toolbar-widget { .modal.popup-editor-dialog .classic-toolbar-widget {
position: sticky; position: sticky;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
background: var(--modal-background-color); background: var(--modal-background-color);
z-index: 998; z-index: 998;
} }

View File

@@ -91,7 +91,7 @@ export default function RevisionsDialog() {
</> </>
) )
} }
footer={<RevisionFooter note={note} />} footer={<RevisionFooter note={note} />}
footerStyle={{ paddingTop: 0, paddingBottom: 0 }} footerStyle={{ paddingTop: 0, paddingBottom: 0 }}
onHidden={() => { onHidden={() => {
setShown(false); setShown(false);
@@ -115,16 +115,16 @@ export default function RevisionsDialog() {
<div className="revision-content-wrapper" style={{ <div className="revision-content-wrapper" style={{
flexGrow: "1", flexGrow: "1",
marginLeft: "20px", marginInlineStart: "20px",
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
maxWidth: "calc(100% - 150px)", maxWidth: "calc(100% - 150px)",
minWidth: 0 minWidth: 0
}}> }}>
<RevisionPreview <RevisionPreview
noteContent={noteContent} noteContent={noteContent}
revisionItem={currentRevision} revisionItem={currentRevision}
showDiff={showDiff} showDiff={showDiff}
setShown={setShown} setShown={setShown}
onRevisionDeleted={() => { onRevisionDeleted={() => {
setRefreshCounter(c => c + 1); setRefreshCounter(c => c + 1);
@@ -138,7 +138,7 @@ export default function RevisionsDialog() {
function RevisionsList({ revisions, onSelect, currentRevision }: { revisions: RevisionItem[], onSelect: (val: string) => void, currentRevision?: RevisionItem }) { function RevisionsList({ revisions, onSelect, currentRevision }: { revisions: RevisionItem[], onSelect: (val: string) => void, currentRevision?: RevisionItem }) {
return ( return (
<FormList onSelect={onSelect} fullHeight> <FormList onSelect={onSelect} fullHeight>
{revisions.map((item) => {revisions.map((item) =>
<FormListItem <FormListItem
title={t("revisions.revision_last_edited", { date: item.dateLastEdited })} title={t("revisions.revision_last_edited", { date: item.dateLastEdited })}
value={item.revisionId} value={item.revisionId}
@@ -152,7 +152,7 @@ function RevisionsList({ revisions, onSelect, currentRevision }: { revisions: Re
function RevisionPreview({noteContent, revisionItem, showDiff, setShown, onRevisionDeleted }: { function RevisionPreview({noteContent, revisionItem, showDiff, setShown, onRevisionDeleted }: {
noteContent?: string, noteContent?: string,
revisionItem?: RevisionItem, revisionItem?: RevisionItem,
showDiff: boolean, showDiff: boolean,
setShown: Dispatch<StateUpdater<boolean>>, setShown: Dispatch<StateUpdater<boolean>>,
onRevisionDeleted?: () => void onRevisionDeleted?: () => void
@@ -163,7 +163,7 @@ function RevisionPreview({noteContent, revisionItem, showDiff, setShown, onRevis
if (revisionItem) { if (revisionItem) {
server.get<RevisionPojo>(`revisions/${revisionItem.revisionId}`).then(setFullRevision); server.get<RevisionPojo>(`revisions/${revisionItem.revisionId}`).then(setFullRevision);
} else { } else {
setFullRevision(undefined); setFullRevision(undefined);
} }
}, [revisionItem]); }, [revisionItem]);
@@ -242,11 +242,11 @@ function RevisionContent({ noteContent, revisionItem, fullRevision, showDiff }:
return <RevisionContentText content={content} /> return <RevisionContentText content={content} />
case "code": case "code":
return <pre style={CODE_STYLE}>{content}</pre>; return <pre style={CODE_STYLE}>{content}</pre>;
case "image": case "image":
switch (revisionItem.mime) { switch (revisionItem.mime) {
case "image/svg+xml": { case "image/svg+xml": {
//Base64 of other format images may be embedded in svg //Base64 of other format images may be embedded in svg
const encodedSVG = encodeURIComponent(content as string); const encodedSVG = encodeURIComponent(content as string);
return <img return <img
src={`data:${fullRevision.mime};utf8,${encodedSVG}`} src={`data:${fullRevision.mime};utf8,${encodedSVG}`}
style={IMAGE_STYLE} />; style={IMAGE_STYLE} />;
@@ -355,7 +355,7 @@ function RevisionFooter({ note }: { note?: FNote }) {
if (revisionsNumberLimit === -1) { if (revisionsNumberLimit === -1) {
revisionsNumberLimit = "∞"; revisionsNumberLimit = "∞";
} }
return <> return <>
<span class="revisions-snapshot-interval flex-grow-1 my-0 py-0"> <span class="revisions-snapshot-interval flex-grow-1 my-0 py-0">
{t("revisions.snapshot_interval", { seconds: options.getInt("revisionSnapshotTimeInterval") })} {t("revisions.snapshot_interval", { seconds: options.getInt("revisionSnapshotTimeInterval") })}
@@ -376,4 +376,4 @@ async function getNote(noteId?: string | null) {
} else { } else {
return appContext.tabManager.getActiveContextNote(); return appContext.tabManager.getActiveContextNote();
} }
} }

View File

@@ -32,7 +32,7 @@ const TPL = /*html*/`
} }
.find-widget-box > *, .replace-widget-box > *{ .find-widget-box > *, .replace-widget-box > *{
margin-right: 15px; margin-inline-end: 15px;
} }
.find-widget-box, .replace-widget-box { .find-widget-box, .replace-widget-box {
@@ -167,7 +167,7 @@ export default class FindWidget extends NoteContextAwareWidget {
if (isIMEComposing(e.originalEvent as KeyboardEvent)) { if (isIMEComposing(e.originalEvent as KeyboardEvent)) {
return; return;
} }
if ((e.metaKey || e.ctrlKey) && (e.key === "F" || e.key === "f")) { if ((e.metaKey || e.ctrlKey) && (e.key === "F" || e.key === "f")) {
// If ctrl+f is pressed when the findbox is shown, select the // If ctrl+f is pressed when the findbox is shown, select the
// whole input to find // whole input to find

View File

@@ -24,7 +24,7 @@ const TPL = /*html*/`<div class="highlights-list-widget">
} }
.highlights-list > ol { .highlights-list > ol {
padding-left: 20px; padding-inline-start: 20px;
} }
.highlights-list li { .highlights-list li {

View File

@@ -2090,14 +2090,14 @@ const icons: Icon[] = [
term: ["keypad"] term: ["keypad"]
}, },
{ {
name: "border-right", name: "border-inline-end",
slug: "border-right-regular", slug: "border-inline-end-regular",
category_id: 111, category_id: 111,
type_of_icon: "REGULAR" type_of_icon: "REGULAR"
}, },
{ {
name: "border-left", name: "border-inline-start",
slug: "border-left-regular", slug: "border-inline-start-regular",
category_id: 111, category_id: 111,
type_of_icon: "REGULAR" type_of_icon: "REGULAR"
}, },

View File

@@ -9,7 +9,7 @@ import { formatMarkdown, applyHighlighting } from "./utils.js";
export const TPL = ` export const TPL = `
<div class="note-context-chat h-100 w-100 d-flex flex-column"> <div class="note-context-chat h-100 w-100 d-flex flex-column">
<!-- Move validation warning outside the card with better styling --> <!-- Move validation warning outside the card with better styling -->
<div class="provider-validation-warning alert alert-warning m-2 border-left border-warning" style="display: none; padding-left: 15px; border-left: 4px solid #ffc107; background-color: rgba(255, 248, 230, 0.9); font-size: 0.9rem; box-shadow: 0 2px 5px rgba(0,0,0,0.05);"></div> <div class="provider-validation-warning alert alert-warning m-2 border-inline-start border-warning" style="display: none; padding-inline-start: 15px; border-inline-start: 4px solid #ffc107; background-color: rgba(255, 248, 230, 0.9); font-size: 0.9rem; box-shadow: 0 2px 5px rgba(0,0,0,0.05);"></div>
<div class="note-context-chat-container flex-grow-1 overflow-auto p-3"> <div class="note-context-chat-container flex-grow-1 overflow-auto p-3">
<div class="note-context-chat-messages"></div> <div class="note-context-chat-messages"></div>

View File

@@ -25,7 +25,7 @@ export default function MobileDetailMenu() {
{ title: t("mobile_detail_menu.insert_child_note"), command: "insertChildNote", uiIcon: "bx bx-plus", enabled: note?.type !== "search" }, { title: t("mobile_detail_menu.insert_child_note"), command: "insertChildNote", uiIcon: "bx bx-plus", enabled: note?.type !== "search" },
{ title: t("mobile_detail_menu.delete_this_note"), command: "delete", uiIcon: "bx bx-trash", enabled: note?.noteId !== "root" }, { title: t("mobile_detail_menu.delete_this_note"), command: "delete", uiIcon: "bx bx-trash", enabled: note?.noteId !== "root" },
{ kind: "separator" }, { kind: "separator" },
{ title: "Note revisions", command: "showRevisions", uiIcon: "bx bx-history" } { title: t("mobile_detail_menu.note_revisions"), command: "showRevisions", uiIcon: "bx bx-history" }
], ],
selectMenuItemHandler: async ({ command }) => { selectMenuItemHandler: async ({ command }) => {
if (command === "insertChildNote") { if (command === "insertChildNote") {

View File

@@ -27,6 +27,7 @@ export default class SidebarContainer extends FlexContainer<BasicWidget> {
private backdropEl!: HTMLElement; private backdropEl!: HTMLElement;
private originalSidebarTransition: string; private originalSidebarTransition: string;
private originalBackdropTransition: string; private originalBackdropTransition: string;
private screenWidth: number;
constructor(screenName: Screen, direction: FlexDirection) { constructor(screenName: Screen, direction: FlexDirection) {
super(direction); super(direction);
@@ -37,6 +38,7 @@ export default class SidebarContainer extends FlexContainer<BasicWidget> {
this.dragState = DRAG_STATE_NONE; this.dragState = DRAG_STATE_NONE;
this.originalSidebarTransition = ""; this.originalSidebarTransition = "";
this.originalBackdropTransition = ""; this.originalBackdropTransition = "";
this.screenWidth = document.body.getBoundingClientRect().width;
} }
doRender() { doRender() {
@@ -51,7 +53,9 @@ export default class SidebarContainer extends FlexContainer<BasicWidget> {
const x = "touches" in e ? e.touches[0].clientX : e.clientX; const x = "touches" in e ? e.touches[0].clientX : e.clientX;
this.startX = x; this.startX = x;
if (x > 30 && this.currentTranslate === -100) { // Prevent dragging if too far from the edge of the screen and the menu is closed.
let dragRefX = glob.isRtl ? this.screenWidth - x : x;
if (dragRefX > 30 && this.currentTranslate === -100) {
return; return;
} }
@@ -66,7 +70,7 @@ export default class SidebarContainer extends FlexContainer<BasicWidget> {
} }
const x = "touches" in e ? e.touches[0].clientX : e.clientX; const x = "touches" in e ? e.touches[0].clientX : e.clientX;
const deltaX = x - this.startX; const deltaX = glob.isRtl ? this.startX - x : x - this.startX;
if (this.dragState === DRAG_STATE_INITIAL_DRAG) { if (this.dragState === DRAG_STATE_INITIAL_DRAG) {
if (this.currentTranslate === -100 ? deltaX > DRAG_CLOSED_START_THRESHOLD : deltaX < -DRAG_OPENED_START_THRESHOLD) { if (this.currentTranslate === -100 ? deltaX > DRAG_CLOSED_START_THRESHOLD : deltaX < -DRAG_OPENED_START_THRESHOLD) {
/* Disable the transitions since they affect performance, they are going to reenabled once drag ends. */ /* Disable the transitions since they affect performance, they are going to reenabled once drag ends. */
@@ -85,10 +89,15 @@ export default class SidebarContainer extends FlexContainer<BasicWidget> {
} }
} else if (this.dragState === DRAG_STATE_DRAGGING) { } else if (this.dragState === DRAG_STATE_DRAGGING) {
const width = this.sidebarEl.offsetWidth; const width = this.sidebarEl.offsetWidth;
const translatePercentage = Math.min(0, Math.max(this.currentTranslate + (deltaX / width) * 100, -100)); let translatePercentage = Math.min(0, Math.max(this.currentTranslate + (deltaX / width) * 100, -100));
const backdropOpacity = Math.max(0, 1 + translatePercentage / 100);
this.translatePercentage = translatePercentage; this.translatePercentage = translatePercentage;
this.sidebarEl.style.transform = `translateX(${translatePercentage}%)`; if (glob.isRtl) {
this.backdropEl.style.opacity = String(Math.max(0, 1 + translatePercentage / 100)); this.sidebarEl.style.transform = `translateX(${-translatePercentage}%)`;
} else {
this.sidebarEl.style.transform = `translateX(${translatePercentage}%)`;
}
this.backdropEl.style.opacity = String(backdropOpacity);
} }
// Consume the event to prevent the user from doing the back to previous page gesture on iOS. // Consume the event to prevent the user from doing the back to previous page gesture on iOS.
@@ -149,7 +158,15 @@ export default class SidebarContainer extends FlexContainer<BasicWidget> {
} }
this.sidebarEl.classList.toggle("show", isOpen); this.sidebarEl.classList.toggle("show", isOpen);
this.sidebarEl.style.transform = isOpen ? "translateX(0)" : "translateX(-100%)"; if (isOpen) {
this.sidebarEl.style.transform = "translateX(0)";
} else {
if (glob.isRtl) {
this.sidebarEl.style.transform = "translateX(100%)"
} else {
this.sidebarEl.style.transform = "translateX(-100%)";
}
}
this.sidebarEl.style.transition = this.originalSidebarTransition; this.sidebarEl.style.transition = this.originalSidebarTransition;
this.backdropEl.classList.toggle("show", isOpen); this.backdropEl.classList.toggle("show", isOpen);

View File

@@ -1,6 +1,6 @@
.note-icon-widget { .note-icon-widget {
padding-left: 7px; padding-inline-start: 7px;
margin-right: 0; margin-inline-end: 0;
width: 50px; width: 50px;
height: 50px; height: 50px;
} }
@@ -29,15 +29,15 @@
.note-icon-widget .filter-row { .note-icon-widget .filter-row {
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
padding-right: 20px; padding-inline-end: 20px;
display: flex; display: flex;
align-items: baseline; align-items: baseline;
} }
.note-icon-widget .filter-row span { .note-icon-widget .filter-row span {
display: block; display: block;
padding-left: 15px; padding-inline-start: 15px;
padding-right: 15px; padding-inline-end: 15px;
font-weight: bold; font-weight: bold;
} }

View File

@@ -38,7 +38,7 @@ const TPL = /*html*/`<div class="note-map-widget">
/* removing default appearance */ /* removing default appearance */
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
margin-left: 15px; margin-inline-start: 15px;
width: 150px; width: 150px;
} }

View File

@@ -61,7 +61,7 @@ const TPL = /*html*/`
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
justify-content: flex-end; justify-content: flex-end;
right: 17px; inset-inline-end: 17px;
border-radius: 7px; border-radius: 7px;
border: 1px solid var(--main-border-color); border: 1px solid var(--main-border-color);
} }
@@ -82,15 +82,15 @@ const TPL = /*html*/`
} }
.collapse-tree-button { .collapse-tree-button {
right: 100px; inset-inline-end: 100px;
} }
.scroll-to-active-note-button { .scroll-to-active-note-button {
right: 55px; inset-inline-end: 55px;
} }
.tree-settings-button { .tree-settings-button {
right: 10px; inset-inline-end: 10px;
} }
.tree-settings-popup { .tree-settings-popup {

View File

@@ -45,7 +45,7 @@ const TPL = /*html*/`
} }
.promoted-attribute-cell div.input-group { .promoted-attribute-cell div.input-group {
margin-left: 10px; margin-inline-start: 10px;
display: flex; display: flex;
min-height: 40px; min-height: 40px;
} }
@@ -95,8 +95,8 @@ const TPL = /*html*/`
content: ""; content: "";
position: absolute; position: absolute;
top: 10px; top: 10px;
left: 0px; inset-inline-start: 0px;
right: 0; inset-inline-end: 0;
height: 2px; height: 2px;
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
transform: rotate(45deg); transform: rotate(45deg);

View File

@@ -2,7 +2,7 @@ import BasicWidget from "./basic_widget.js";
import server from "../services/server.js"; import server from "../services/server.js";
import linkService from "../services/link.js"; import linkService from "../services/link.js";
import froca from "../services/froca.js"; import froca from "../services/froca.js";
import utils from "../services/utils.js"; import utils, { handleRightToLeftPlacement } from "../services/utils.js";
import appContext from "../components/app_context.js"; import appContext from "../components/app_context.js";
import shortcutService, { isIMEComposing } from "../services/shortcuts.js"; import shortcutService, { isIMEComposing } from "../services/shortcuts.js";
import { t } from "../services/i18n.js"; import { t } from "../services/i18n.js";
@@ -15,12 +15,12 @@ const TPL = /*html*/`
padding: 10px 10px 10px 0px; padding: 10px 10px 10px 0px;
height: 50px; height: 50px;
} }
.quick-search button, .quick-search input { .quick-search button, .quick-search input {
border: 0; border: 0;
font-size: 100% !important; font-size: 100% !important;
} }
.quick-search .dropdown-menu { .quick-search .dropdown-menu {
--quick-search-item-delimiter-color: var(--dropdown-border-color); --quick-search-item-delimiter-color: var(--dropdown-border-color);
@@ -32,40 +32,40 @@ const TPL = /*html*/`
text-overflow: ellipsis; text-overflow: ellipsis;
box-shadow: -30px 50px 93px -50px black; box-shadow: -30px 50px 93px -50px black;
} }
.quick-search .dropdown-item { .quick-search .dropdown-item {
white-space: normal; white-space: normal;
padding: 12px 16px; padding: 12px 16px;
line-height: 1.4; line-height: 1.4;
position: relative; position: relative;
} }
.quick-search .dropdown-item + .dropdown-item::after { .quick-search .dropdown-item + .dropdown-item::after {
content: ''; content: '';
position: absolute; position: absolute;
left: 0; inset-inline-start: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 1px; height: 1px;
border-bottom: 1px solid var(--quick-search-item-delimiter-color); border-bottom: 1px solid var(--quick-search-item-delimiter-color);
} }
.quick-search .dropdown-item:last-child::after { .quick-search .dropdown-item:last-child::after {
display: none; display: none;
} }
.quick-search .dropdown-item.disabled::after { .quick-search .dropdown-item.disabled::after {
display: none; display: none;
} }
.quick-search .dropdown-item.show-in-full-search::after { .quick-search .dropdown-item.show-in-full-search::after {
display: none; display: none;
} }
.quick-search-item.dropdown-item:hover { .quick-search-item.dropdown-item:hover {
background-color: #f8f9fa; background-color: #f8f9fa;
} }
.quick-search .quick-search-item { .quick-search .quick-search-item {
width: 100%; width: 100%;
} }
@@ -151,7 +151,7 @@ export default class QuickSearchWidget extends BasicWidget {
private dropdown!: bootstrap.Dropdown; private dropdown!: bootstrap.Dropdown;
private $searchString!: JQuery<HTMLElement>; private $searchString!: JQuery<HTMLElement>;
private $dropdownMenu!: JQuery<HTMLElement>; private $dropdownMenu!: JQuery<HTMLElement>;
// State for infinite scrolling // State for infinite scrolling
private allSearchResults: Array<any> = []; private allSearchResults: Array<any> = [];
private allSearchResultNoteIds: string[] = []; private allSearchResultNoteIds: string[] = [];
@@ -172,7 +172,7 @@ export default class QuickSearchWidget extends BasicWidget {
}); });
this.$widget.find(".input-group-prepend").on("shown.bs.dropdown", () => this.search()); this.$widget.find(".input-group-prepend").on("shown.bs.dropdown", () => this.search());
// Add scroll event listener for infinite scrolling // Add scroll event listener for infinite scrolling
this.$dropdownMenu.on("scroll", () => { this.$dropdownMenu.on("scroll", () => {
this.handleScroll(); this.handleScroll();
@@ -187,7 +187,7 @@ export default class QuickSearchWidget extends BasicWidget {
if (originalEvent && isIMEComposing(originalEvent)) { if (originalEvent && isIMEComposing(originalEvent)) {
return; return;
} }
if (e.which === 13) { if (e.which === 13) {
if (this.$dropdownMenu.is(":visible")) { if (this.$dropdownMenu.is(":visible")) {
this.search(); // just update already visible dropdown this.search(); // just update already visible dropdown
@@ -248,7 +248,7 @@ export default class QuickSearchWidget extends BasicWidget {
let tooltip = new Tooltip(this.$searchString[0], { let tooltip = new Tooltip(this.$searchString[0], {
trigger: "manual", trigger: "manual",
title: `Search error: ${error}`, title: `Search error: ${error}`,
placement: "right" placement: handleRightToLeftPlacement("right")
}); });
tooltip.show(); tooltip.show();
@@ -293,40 +293,40 @@ export default class QuickSearchWidget extends BasicWidget {
if (!noteId) continue; if (!noteId) continue;
const $item = $('<a class="dropdown-item" tabindex="0" href="javascript:">'); const $item = $('<a class="dropdown-item" tabindex="0" href="javascript:">');
// Build the display HTML with content snippet below the title // Build the display HTML with content snippet below the title
let itemHtml = `<div class="quick-search-item"> let itemHtml = `<div class="quick-search-item">
<div class="quick-search-item-header"> <div class="quick-search-item-header">
<span class="quick-search-item-icon ${result.icon}"></span> <span class="quick-search-item-icon ${result.icon}"></span>
<span class="search-result-title">${result.highlightedNotePathTitle}</span> <span class="search-result-title">${result.highlightedNotePathTitle}</span>
</div>`; </div>`;
// Add attribute snippet (tags/attributes) below the title if available // Add attribute snippet (tags/attributes) below the title if available
if (result.highlightedAttributeSnippet) { if (result.highlightedAttributeSnippet) {
// Replace <br> with a blank space to join the atributes on the same single line // Replace <br> with a blank space to join the atributes on the same single line
const snippet = (result.highlightedAttributeSnippet as string).replace(/<br\s?\/?>/g, " "); const snippet = (result.highlightedAttributeSnippet as string).replace(/<br\s?\/?>/g, " ");
itemHtml += `<div class="search-result-attributes">${snippet}</div>`; itemHtml += `<div class="search-result-attributes">${snippet}</div>`;
} }
// Add content snippet below the attributes if available // Add content snippet below the attributes if available
if (result.highlightedContentSnippet) { if (result.highlightedContentSnippet) {
itemHtml += `<div class="search-result-content">${result.highlightedContentSnippet}</div>`; itemHtml += `<div class="search-result-content">${result.highlightedContentSnippet}</div>`;
} }
itemHtml += `</div>`; itemHtml += `</div>`;
$item.html(itemHtml); $item.html(itemHtml);
$item.on("click", (e) => { $item.on("click", (e) => {
this.dropdown.hide(); this.dropdown.hide();
e.preventDefault(); e.preventDefault();
const activeContext = appContext.tabManager.getActiveContext(); const activeContext = appContext.tabManager.getActiveContext();
if (activeContext) { if (activeContext) {
activeContext.setNote(noteId); activeContext.setNote(noteId);
} }
}); });
shortcutService.bindElShortcut($item, "return", () => { shortcutService.bindElShortcut($item, "return", () => {
this.dropdown.hide(); this.dropdown.hide();
@@ -390,7 +390,7 @@ export default class QuickSearchWidget extends BasicWidget {
// Trigger loading more when user scrolls near the bottom (within 50px) // Trigger loading more when user scrolls near the bottom (within 50px)
if (scrollTop + clientHeight >= scrollHeight - 50) { if (scrollTop + clientHeight >= scrollHeight - 50) {
const totalResults = this.allSearchResults.length > 0 ? this.allSearchResults.length : this.allSearchResultNoteIds.length; const totalResults = this.allSearchResults.length > 0 ? this.allSearchResults.length : this.allSearchResultNoteIds.length;
if (this.currentDisplayedCount < totalResults) { if (this.currentDisplayedCount < totalResults) {
this.displayMoreResults(LOAD_MORE_BATCH_SIZE).then(() => { this.displayMoreResults(LOAD_MORE_BATCH_SIZE).then(() => {
this.addShowInFullSearchButton(); this.addShowInFullSearchButton();

View File

@@ -5,7 +5,7 @@ import { useEffect, useMemo, useRef, useState, type CSSProperties } from "preact
import "./FormList.css"; import "./FormList.css";
import { CommandNames } from "../../components/app_context"; import { CommandNames } from "../../components/app_context";
import { useStaticTooltip } from "./hooks"; import { useStaticTooltip } from "./hooks";
import { isMobile } from "../../services/utils"; import { handleRightToLeftPlacement, isMobile } from "../../services/utils";
interface FormListOpts { interface FormListOpts {
children: ComponentChildren; children: ComponentChildren;
@@ -22,7 +22,7 @@ export default function FormList({ children, onSelect, style, fullHeight }: Form
if (!triggerRef.current || !wrapperRef.current) { if (!triggerRef.current || !wrapperRef.current) {
return; return;
} }
const $wrapperRef = $(wrapperRef.current); const $wrapperRef = $(wrapperRef.current);
const dropdown = BootstrapDropdown.getOrCreateInstance(triggerRef.current); const dropdown = BootstrapDropdown.getOrCreateInstance(triggerRef.current);
$wrapperRef.on("hide.bs.dropdown", (e) => e.preventDefault()); $wrapperRef.on("hide.bs.dropdown", (e) => e.preventDefault());
@@ -93,8 +93,8 @@ interface FormListItemOpts {
} }
const TOOLTIP_CONFIG: Partial<Tooltip.Options> = { const TOOLTIP_CONFIG: Partial<Tooltip.Options> = {
placement: "right", placement: handleRightToLeftPlacement("right"),
fallbackPlacements: [ "right" ] fallbackPlacements: [ handleRightToLeftPlacement("right") ]
} }
export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, ...contentProps }: FormListItemOpts) { export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, ...contentProps }: FormListItemOpts) {
@@ -178,4 +178,4 @@ export function FormDropdownSubmenu({ icon, title, children }: { icon: string, t
</ul> </ul>
</li> </li>
) )
} }

View File

@@ -18,7 +18,7 @@
.switch-widget .switch-button { .switch-widget .switch-button {
display: block; display: block;
position: relative; position: relative;
margin-left: 8px; margin-inline-start: 8px;
width: var(--switch-track-width); width: var(--switch-track-width);
height: var(--switch-track-height); height: var(--switch-track-height);
border-radius: 24px; border-radius: 24px;
@@ -40,7 +40,7 @@
content: ""; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
width: var(--switch-thumb-width); width: var(--switch-thumb-width);
height: var(--switch-thumb-height); height: var(--switch-thumb-height);
background-color: var(--switch-off-thumb-background); background-color: var(--switch-off-thumb-background);
@@ -50,6 +50,10 @@
background 200ms ease-out; background 200ms ease-out;
} }
body[dir=rtl] .switch-widget .switch-button:after {
--x: calc(var(--y) * -1);
}
.switch-widget .switch-button.on:after { .switch-widget .switch-button.on:after {
--x: calc(var(--switch-track-width) - var(--switch-thumb-width) - var(--y)); --x: calc(var(--switch-track-width) - var(--switch-thumb-width) - var(--y));
@@ -58,12 +62,16 @@
background 100ms ease-in; background 100ms ease-in;
} }
body[dir=rtl] .switch-widget .switch-button.on:after {
--x: calc((var(--switch-track-width) - var(--switch-thumb-width) - var(--y)) * -1);
}
.switch-widget .switch-button input[type="checkbox"] { .switch-widget .switch-button input[type="checkbox"] {
/* A hidden check box for accesibility purposes */ /* A hidden check box for accesibility purposes */
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; inset-inline-start: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
opacity: 0; opacity: 0;
@@ -86,7 +94,7 @@
.switch-widget .switch-help-button { .switch-widget .switch-help-button {
border: 0; border: 0;
margin-left: 4px; margin-inline-start: 4px;
background: none; background: none;
cursor: pointer; cursor: pointer;
font-size: 1.1em; font-size: 1.1em;

View File

@@ -353,7 +353,7 @@ function NoteLanguageSwitch({ note }: { note?: FNote | null }) {
>{t("note_language.configure-languages")}</FormListItem> >{t("note_language.configure-languages")}</FormListItem>
</Dropdown> </Dropdown>
<HelpButton helpPage="B0lcI9xz1r8K" style={{ marginLeft: "4px" }} /> <HelpButton helpPage="B0lcI9xz1r8K" style={{ marginInlineStart: "4px" }} />
<Modal <Modal
className="content-languages-modal" className="content-languages-modal"

View File

@@ -263,7 +263,7 @@ function AncestorOption({ note, ...restProps}: SearchOptionProps) {
placeholder={t("ancestor.placeholder")} placeholder={t("ancestor.placeholder")}
/> />
<div style="margin-left: 10px; margin-right: 10px">{t("ancestor.depth_label")}:</div> <div style="margin-inline-start: 10px; margin-inline-end: 10px">{t("ancestor.depth_label")}:</div>
<FormSelect <FormSelect
values={options} values={options}
keyProperty="value" titleProperty="label" keyProperty="value" titleProperty="label"

View File

@@ -4,7 +4,7 @@ import { t } from "../../../services/i18n";
import server from "../../../services/server"; import server from "../../../services/server";
import note_autocomplete, { Suggestion } from "../../../services/note_autocomplete"; import note_autocomplete, { Suggestion } from "../../../services/note_autocomplete";
import CKEditor, { CKEditorApi } from "../../react/CKEditor"; import CKEditor, { CKEditorApi } from "../../react/CKEditor";
import { useLegacyImperativeHandlers, useLegacyWidget, useTooltip, useTriliumEvent } from "../../react/hooks"; import { useLegacyImperativeHandlers, useLegacyWidget, useTooltip, useTriliumEvent, useTriliumOption } from "../../react/hooks";
import FAttribute from "../../../entities/fattribute"; import FAttribute from "../../../entities/fattribute";
import attribute_renderer from "../../../services/attribute_renderer"; import attribute_renderer from "../../../services/attribute_renderer";
import FNote from "../../../entities/fnote"; import FNote from "../../../entities/fnote";
@@ -100,6 +100,7 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
const currentValueRef = useRef(currentValue); const currentValueRef = useRef(currentValue);
const wrapperRef = useRef<HTMLDivElement>(null); const wrapperRef = useRef<HTMLDivElement>(null);
const editorRef = useRef<CKEditorApi>(); const editorRef = useRef<CKEditorApi>();
const [ locale ] = useTriliumOption("locale");
const { showTooltip, hideTooltip } = useTooltip(wrapperRef, { const { showTooltip, hideTooltip } = useTooltip(wrapperRef, {
trigger: "focus", trigger: "focus",
@@ -308,7 +309,8 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
toolbar: { items: [] }, toolbar: { items: [] },
placeholder: t("attribute_editor.placeholder"), placeholder: t("attribute_editor.placeholder"),
mention: { feeds: mentionSetup }, mention: { feeds: mentionSetup },
licenseKey: "GPL" licenseKey: "GPL",
language: "en"
}} }}
onChange={(currentValue) => { onChange={(currentValue) => {
currentValueRef.current = currentValue ?? ""; currentValueRef.current = currentValue ?? "";

View File

@@ -11,7 +11,7 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
margin-left: 10px; margin-inline-start: 10px;
flex-grow: 1; flex-grow: 1;
flex-flow: row wrap; flex-flow: row wrap;
} }
@@ -49,7 +49,7 @@
} }
.ribbon-tab-title:first-of-type { .ribbon-tab-title:first-of-type {
padding-left: 10px; padding-inline-start: 10px;
} }
.ribbon-tab-spacer { .ribbon-tab-spacer {
@@ -70,19 +70,19 @@
.ribbon-button-container { .ribbon-button-container {
display: flex; display: flex;
border-bottom: 1px solid var(--main-border-color); border-bottom: 1px solid var(--main-border-color);
margin-right: 5px; margin-inline-end: 5px;
} }
.ribbon-button-container > * { .ribbon-button-container > * {
position: relative; position: relative;
top: -3px; top: -3px;
margin-left: 10px; margin-inline-start: 10px;
} }
.ribbon-body { .ribbon-body {
border-bottom: 1px solid var(--main-border-color); border-bottom: 1px solid var(--main-border-color);
margin-left: 10px; margin-inline-start: 10px;
margin-right: 5px; /* needs to have this value so that the bottom border is the same width as the top one */ margin-inline-end: 5px; /* needs to have this value so that the bottom border is the same width as the top one */
} }
.ribbon-body.active { .ribbon-body.active {
@@ -105,7 +105,7 @@
.basic-properties-widget > * { .basic-properties-widget > * {
margin-top: 9px; margin-top: 9px;
margin-bottom: 2px; margin-bottom: 2px;
margin-right: 30px; margin-inline-end: 30px;
} }
.note-type-container, .note-type-container,
@@ -262,7 +262,7 @@
.note-map-ribbon-widget .open-full-button, .note-map-ribbon-widget .collapse-button { .note-map-ribbon-widget .open-full-button, .note-map-ribbon-widget .collapse-button {
position: absolute; position: absolute;
right: 5px; inset-inline-end: 5px;
bottom: 5px; bottom: 5px;
z-index: 1000; z-index: 1000;
} }
@@ -285,6 +285,11 @@
transition: opacity .1s linear; transition: opacity .1s linear;
} }
body[dir=rtl] .attribute-list-editor {
padding: 0 5px 0 100px !important;
text-align: right !important;
}
.attribute-list-editor.ck-content .mention { .attribute-list-editor.ck-content .mention {
color: var(--muted-text-color) !important; color: var(--muted-text-color) !important;
background: transparent !important; background: transparent !important;
@@ -294,7 +299,7 @@
display: flex; display: flex;
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; inset-inline-end: 0;
bottom: 0; bottom: 0;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
@@ -308,8 +313,8 @@
/* #region Owned Attributes */ /* #region Owned Attributes */
.attribute-list { .attribute-list {
margin-left: 7px; margin-inline-start: 7px;
margin-right: 7px; margin-inline-end: 7px;
margin-top: 5px; margin-top: 5px;
margin-bottom: 2px; margin-bottom: 2px;
position: relative; position: relative;
@@ -359,7 +364,7 @@
} }
.book-properties-widget input[type="checkbox"] { .book-properties-widget input[type="checkbox"] {
margin-right: 5px; margin-inline-end: 5px;
} }
.book-properties-widget label { .book-properties-widget label {
@@ -394,7 +399,7 @@
/* minimal width so that table remains static sized and most space remains for middle column with settings */ /* minimal width so that table remains static sized and most space remains for middle column with settings */
width: 50px; width: 50px;
white-space: nowrap; white-space: nowrap;
text-align: right; text-align: end;
vertical-align: middle; vertical-align: middle;
} }
@@ -446,7 +451,7 @@
position: relative; position: relative;
top: 3px; top: 3px;
font-size: 120%; font-size: 120%;
margin-right: 5px; margin-inline-end: 5px;
} }
.note-actions .dropdown-item[disabled], .note-actions .dropdown-item[disabled]:hover { .note-actions .dropdown-item[disabled], .note-actions .dropdown-item[disabled]:hover {

View File

@@ -1,6 +1,6 @@
.sql-table-schemas-widget { .sql-table-schemas-widget {
padding: 12px; padding: 12px;
padding-right: 10%; padding-inline-end: 10%;
contain: none !important; contain: none !important;
} }

View File

@@ -3,7 +3,7 @@ import BasicWidget from "./basic_widget.js";
import ws from "../services/ws.js"; import ws from "../services/ws.js";
import options from "../services/options.js"; import options from "../services/options.js";
import syncService from "../services/sync.js"; import syncService from "../services/sync.js";
import { escapeQuotes } from "../services/utils.js"; import { escapeQuotes, handleRightToLeftPlacement } from "../services/utils.js";
import { Tooltip } from "bootstrap"; import { Tooltip } from "bootstrap";
import { WebSocketMessage } from "@triliumnext/commons"; import { WebSocketMessage } from "@triliumnext/commons";
@@ -27,7 +27,7 @@ const TPL = /*html*/`
.sync-status .sync-status-sub-icon { .sync-status .sync-status-sub-icon {
font-size: 40%; font-size: 40%;
position: absolute; position: absolute;
left: 0; inset-inline-start: 0;
top: 16px; top: 16px;
} }
@@ -109,8 +109,8 @@ export default class SyncStatusWidget extends BasicWidget {
Tooltip.getOrCreateInstance(this.$widget.find(`.sync-status-${className}`)[0], { Tooltip.getOrCreateInstance(this.$widget.find(`.sync-status-${className}`)[0], {
html: true, html: true,
placement: this.settings.titlePlacement, placement: handleRightToLeftPlacement(this.settings.titlePlacement),
fallbackPlacements: [this.settings.titlePlacement] fallbackPlacements: [ handleRightToLeftPlacement(this.settings.titlePlacement) ]
}); });
this.$widget.show(); this.$widget.show();

View File

@@ -155,8 +155,8 @@ const TAB_ROW_TPL = `
} }
.tab-row-widget .note-tab[is-mini] .note-tab-wrapper { .tab-row-widget .note-tab[is-mini] .note-tab-wrapper {
padding-left: 2px; padding-inline-start: 2px;
padding-right: 2px; padding-inline-end: 2px;
} }
.tab-row-widget .note-tab .note-tab-title { .tab-row-widget .note-tab .note-tab-title {
@@ -168,11 +168,11 @@ const TAB_ROW_TPL = `
.tab-row-widget .note-tab .note-tab-icon { .tab-row-widget .note-tab .note-tab-icon {
position: relative; position: relative;
padding-right: 3px; padding-inline-end: 3px;
} }
.tab-row-widget .note-tab[is-small] .note-tab-title { .tab-row-widget .note-tab[is-small] .note-tab-title {
margin-left: 0; margin-inline-start: 0;
} }
.tab-row-widget .note-tab .note-tab-drag-handle { .tab-row-widget .note-tab .note-tab-drag-handle {
@@ -240,14 +240,14 @@ const TAB_ROW_TPL = `
} }
.tab-row-widget .note-tab[is-smaller] .note-tab-close { .tab-row-widget .note-tab[is-smaller] .note-tab-close {
margin-left: auto; margin-inline-start: auto;
} }
.tab-row-widget .note-tab[is-mini]:not([active]) .note-tab-close { .tab-row-widget .note-tab[is-mini]:not([active]) .note-tab-close {
display: none; display: none;
} }
.tab-row-widget .note-tab[is-mini][active] .note-tab-close { .tab-row-widget .note-tab[is-mini][active] .note-tab-close {
margin-left: auto; margin-inline-start: auto;
margin-right: auto; margin-inline-end: auto;
} }
@-moz-keyframes note-tab-was-just-added { @-moz-keyframes note-tab-was-just-added {
to { to {
@@ -500,6 +500,9 @@ export default class TabRowWidget extends BasicWidget {
position -= MARGIN_WIDTH; // the last margin should not be applied position -= MARGIN_WIDTH; // the last margin should not be applied
const anchorPosition = position; const anchorPosition = position;
if (glob.isRtl) {
tabPositions.reverse();
}
return { tabPositions, anchorPosition }; return { tabPositions, anchorPosition };
} }

View File

@@ -29,13 +29,13 @@ const TPL = /*html*/`<div class="toc-widget">
contain: none; contain: none;
overflow: auto; overflow: auto;
position: relative; position: relative;
padding-left:0px !important; padding-inline-start:0px !important;
} }
.toc ol { .toc ol {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
padding-left: 0px; padding-inline-start: 0px;
transition: max-height 0.3s ease; transition: max-height 0.3s ease;
} }
@@ -47,7 +47,7 @@ const TPL = /*html*/`<div class="toc-widget">
content: ""; content: "";
position: absolute; position: absolute;
height: 100%; height: 100%;
border-left: 1px solid var(--main-border-color); border-inline-start: 1px solid var(--main-border-color);
z-index: 10; z-index: 10;
} }
@@ -56,7 +56,7 @@ const TPL = /*html*/`<div class="toc-widget">
position: relative; position: relative;
list-style: none; list-style: none;
align-items: center; align-items: center;
padding-left: 7px; padding-inline-start: 7px;
cursor: pointer; cursor: pointer;
text-align: justify; text-align: justify;
word-wrap: break-word; word-wrap: break-word;
@@ -80,11 +80,11 @@ const TPL = /*html*/`<div class="toc-widget">
} }
.toc > ol ol::before { .toc > ol ol::before {
left: calc((var(--toc-depth-level) - 2) * 20px + 14px); inset-inline-start: calc((var(--toc-depth-level) - 2) * 20px + 14px);
} }
.toc li { .toc li {
padding-left: calc((var(--toc-depth-level) - 1) * 20px + 4px); padding-inline-start: calc((var(--toc-depth-level) - 1) * 20px + 4px);
} }
.toc li .collapse-button { .toc li .collapse-button {
@@ -103,12 +103,12 @@ const TPL = /*html*/`<div class="toc-widget">
} }
.toc li .item-content { .toc li .item-content {
margin-left: 25px; margin-inline-start: 25px;
flex: 1; flex: 1;
} }
.toc li .collapse-button + .item-content { .toc li .collapse-button + .item-content {
margin-left: 4px; margin-inline-start: 4px;
} }
.toc li:hover { .toc li:hover {

View File

@@ -63,7 +63,7 @@ const TPL = /*html*/`\
/* Horizontal layout */ /* Horizontal layout */
.note-detail-split.split-horizontal > .note-detail-split-preview-col { .note-detail-split.split-horizontal > .note-detail-split-preview-col {
border-left: 1px solid var(--main-border-color); border-inline-start: 1px solid var(--main-border-color);
} }
.note-detail-split.split-horizontal > .note-detail-split-editor-col, .note-detail-split.split-horizontal > .note-detail-split-editor-col,

View File

@@ -11,8 +11,8 @@ const TPL = /*html*/`
<div class="attachment-detail note-detail-printable"> <div class="attachment-detail note-detail-printable">
<style> <style>
.attachment-detail { .attachment-detail {
padding-left: 15px; padding-inline-start: 15px;
padding-right: 15px; padding-inline-end: 15px;
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -9,8 +9,8 @@ const TPL = /*html*/`
<div class="attachment-list note-detail-printable"> <div class="attachment-list note-detail-printable">
<style> <style>
.attachment-list { .attachment-list {
padding-left: 15px; padding-inline-start: 15px;
padding-right: 15px; padding-inline-end: 15px;
} }
.attachment-list .links-wrapper { .attachment-list .links-wrapper {

View File

@@ -12,16 +12,16 @@
#root-widget.virtual-keyboard-opened .classic-toolbar-outer-container.ios { #root-widget.virtual-keyboard-opened .classic-toolbar-outer-container.ios {
position: absolute; position: absolute;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
bottom: 0; bottom: 0;
} }
.classic-toolbar-widget { .classic-toolbar-widget {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
height: 38px; height: 38px;
overflow: scroll; overflow: scroll;
display: flex; display: flex;

View File

@@ -21,7 +21,7 @@ const TPL = /*html*/`
<style> <style>
.note-detail-editable-text { .note-detail-editable-text {
font-family: var(--detail-font-family); font-family: var(--detail-font-family);
padding-left: 14px; padding-inline-start: 14px;
padding-top: 10px; padding-top: 10px;
height: 100%; height: 100%;
} }
@@ -32,7 +32,7 @@ const TPL = /*html*/`
} }
body.mobile .note-detail-editable-text { body.mobile .note-detail-editable-text {
padding-left: 4px; padding-inline-start: 4px;
} }
.note-detail-editable-text a:hover { .note-detail-editable-text a:hover {

View File

@@ -29,7 +29,7 @@ const TPL = /*html*/`
.map-container .node-menu { .map-container .node-menu {
position: absolute; position: absolute;
top: 60px; top: 60px;
right: 20px; inset-inline-end: 20px;
bottom: 80px; bottom: 80px;
overflow: auto; overflow: auto;
background: var(--panel-bgcolor); background: var(--panel-bgcolor);

View File

@@ -2,7 +2,7 @@
.options-mime-types { .options-mime-types {
list-style-type: none; list-style-type: none;
column-width: 250px; column-width: 250px;
padding-left: 0; padding-inline-start: 0;
} }
.options-mime-types > section { .options-mime-types > section {
@@ -11,6 +11,6 @@
} }
.options-mime-types ul { .options-mime-types ul {
padding-left: 0; padding-inline-start: 0;
} }
/* #endregion */ /* #endregion */

View File

@@ -23,11 +23,18 @@ export default function InternationalizationOptions() {
} }
function LocalizationOptions() { function LocalizationOptions() {
const { uiLocales, formattingLocales: contentLocales } = useMemo(() => { const { uiLocales, formattingLocales: contentLocales } = useMemo<{ uiLocales: Locale[], formattingLocales: Locale[] }>(() => {
const allLocales = getAvailableLocales(); const allLocales = getAvailableLocales();
return { return {
uiLocales: allLocales.filter(locale => !locale.contentOnly), uiLocales: allLocales.filter(locale => {
formattingLocales: allLocales.filter(locale => locale.electronLocale), if (locale.contentOnly) return false;
if (locale.devOnly && !glob.isDev) return false;
return true;
}),
formattingLocales: [
{ id: "", name: t("i18n.formatting-locale-auto") },
...allLocales.filter(locale => locale.electronLocale)
]
} }
}, []); }, []);
@@ -40,7 +47,7 @@ function LocalizationOptions() {
<LocaleSelector locales={uiLocales} currentValue={locale} onChange={setLocale} /> <LocaleSelector locales={uiLocales} currentValue={locale} onChange={setLocale} />
</OptionsRow> </OptionsRow>
{isElectron() && <OptionsRow name="formatting-locale" label={t("i18n.formatting-locale")}> {<OptionsRow name="formatting-locale" label={t("i18n.formatting-locale")}>
<LocaleSelector locales={contentLocales} currentValue={formattingLocale} onChange={setFormattingLocale} /> <LocaleSelector locales={contentLocales} currentValue={formattingLocale} onChange={setFormattingLocale} />
</OptionsRow>} </OptionsRow>}
@@ -53,7 +60,7 @@ function LocaleSelector({ id, locales, currentValue, onChange }: { id?: string;
return <FormSelect return <FormSelect
id={id} id={id}
values={locales} values={locales}
keyProperty="id" titleProperty="name" keyProperty="id" titleProperty="name"
currentValue={currentValue} onChange={onChange} currentValue={currentValue} onChange={onChange}
/>; />;
} }
@@ -74,7 +81,7 @@ function DateSettings() {
]} ]}
currentValue={firstDayOfWeek} onChange={setFirstDayOfWeek} currentValue={firstDayOfWeek} onChange={setFirstDayOfWeek}
/> />
</OptionsRow> </OptionsRow>
<OptionsRow name="first-week-of-year" label={t("i18n.first-week-of-the-year")}> <OptionsRow name="first-week-of-year" label={t("i18n.first-week-of-the-year")}>
<FormRadioGroup <FormRadioGroup
@@ -93,7 +100,7 @@ function DateSettings() {
keyProperty="days" keyProperty="days"
currentValue={minDaysInFirstWeek} onChange={setMinDaysInFirstWeek} currentValue={minDaysInFirstWeek} onChange={setMinDaysInFirstWeek}
values={Array.from( values={Array.from(
{ length: 7 }, { length: 7 },
(_, i) => ({ days: String(i + 1) }))} /> (_, i) => ({ days: String(i + 1) }))} />
</OptionsRow>} </OptionsRow>}
@@ -139,4 +146,4 @@ export function ContentLanguagesList() {
columnWidth="300px" columnWidth="300px"
/> />
); );
} }

View File

@@ -62,7 +62,7 @@ function FormattingToolbar() {
name="multiline-toolbar" name="multiline-toolbar"
label={t("editing.editor_type.multiline-toolbar")} label={t("editing.editor_type.multiline-toolbar")}
currentValue={textNoteEditorMultilineToolbar} onChange={setTextNoteEditorMultilineToolbar} currentValue={textNoteEditorMultilineToolbar} onChange={setTextNoteEditorMultilineToolbar}
containerStyle={{ marginLeft: "1em" }} containerStyle={{ marginInlineStart: "1em" }}
/> />
</OptionsSection> </OptionsSection>
) )

View File

@@ -41,7 +41,7 @@ const TPL = /*html*/`
} }
body.mobile .note-detail-readonly-text { body.mobile .note-detail-readonly-text {
padding-left: 10px; padding-inline-start: 10px;
} }
.note-detail-readonly-text p:first-child, .note-detail-readonly-text::before { .note-detail-readonly-text p:first-child, .note-detail-readonly-text::before {
@@ -56,7 +56,7 @@ const TPL = /*html*/`
.edit-text-note-button { .edit-text-note-button {
position: absolute; position: absolute;
top: 5px; top: 5px;
right: 10px; inset-inline-end: 10px;
font-size: 150%; font-size: 150%;
padding: 5px; padding: 5px;
cursor: pointer; cursor: pointer;

View File

@@ -30,7 +30,7 @@ async function main() {
// needed for excalidraw export https://github.com/zadam/trilium/issues/4271 // needed for excalidraw export https://github.com/zadam/trilium/issues/4271
app.commandLine.appendSwitch("enable-experimental-web-platform-features"); app.commandLine.appendSwitch("enable-experimental-web-platform-features");
app.commandLine.appendSwitch("lang", options.getOptionOrNull("formattingLocale") ?? "en"); app.commandLine.appendSwitch("lang", options.getOptionOrNull("formattingLocale") || options.getOptionOrNull("locale") || "en");
// Disable smooth scroll if the option is set // Disable smooth scroll if the option is set
const smoothScrollEnabled = options.getOptionOrNull("smoothScrollEnabled"); const smoothScrollEnabled = options.getOptionOrNull("smoothScrollEnabled");

View File

@@ -8,8 +8,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover" />
<link rel="manifest" crossorigin="use-credentials" href="manifest.webmanifest"> <link rel="manifest" crossorigin="use-credentials" href="manifest.webmanifest">
<title>Trilium Notes</title> <title>Trilium Notes</title>
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
</head> </head>
<body id="trilium-app" class="desktop heading-style-<%= headingStyle %> layout-<%= layoutOrientation %> platform-<%= platform %> <%= isElectron ? 'electron' : '' %> <%= hasNativeTitleBar ? 'native-titlebar' : '' %> <%= hasBackgroundEffects ? 'background-effects' : '' %>"> <body
id="trilium-app"
class="desktop heading-style-<%= headingStyle %> layout-<%= layoutOrientation %> platform-<%= platform %> <%= isElectron ? 'electron' : '' %> <%= hasNativeTitleBar ? 'native-titlebar' : '' %> <%= hasBackgroundEffects ? 'background-effects' : '' %>"
lang="<%= currentLocale.id %>" dir="<%= currentLocale.rtl ? 'rtl' : 'ltr' %>"
>
<noscript><%= t("javascript-required") %></noscript> <noscript><%= t("javascript-required") %></noscript>
<script> <script>
@@ -36,7 +41,6 @@
<!-- Required for correct loading of scripts in Electron --> <!-- Required for correct loading of scripts in Electron -->
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script> <script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<link href="<%= appPath %>/bootstrap.css" rel="stylesheet">
<link href="<%= assetPath %>/stylesheets/ckeditor-theme.css" rel="stylesheet"> <link href="<%= assetPath %>/stylesheets/ckeditor-theme.css" rel="stylesheet">
<link href="api/fonts" rel="stylesheet"> <link href="api/fonts" rel="stylesheet">
<link href="<%= assetPath %>/stylesheets/theme-light.css" rel="stylesheet"> <link href="<%= assetPath %>/stylesheets/theme-light.css" rel="stylesheet">
@@ -57,7 +61,6 @@
<link href="<%= assetPath %>/src/boxicons.css" rel="stylesheet"> <link href="<%= assetPath %>/src/boxicons.css" rel="stylesheet">
<link href="<%= assetPath %>/stylesheets/print.css" rel="stylesheet" media="print"> <link href="<%= assetPath %>/stylesheets/print.css" rel="stylesheet" media="print">
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
<script src="<%= appPath %>/desktop.js" crossorigin type="module"></script> <script src="<%= appPath %>/desktop.js" crossorigin type="module"></script>
</body> </body>

View File

@@ -7,18 +7,17 @@
<link rel="apple-touch-icon" sizes="180x180" href="<%= assetPath %>/images/app-icons/ios/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="<%= assetPath %>/images/app-icons/ios/apple-touch-icon.png">
<link rel="shortcut icon" href="favicon.ico"> <link rel="shortcut icon" href="favicon.ico">
<style> <style>
.login-page { body {
/* Prevent the content from being rendered before the main stylesheet loads */ /* Prevent the content from being rendered before the main stylesheet loads */
display: none; display: none;
} }
</style> </style>
<% // TriliumNextTODO: move the css file to ${assetPath}/stylesheets/ %>
<link rel="stylesheet" href="<%= appPath %>/bootstrap.css">
<link rel="stylesheet" href="<%= assetPath %>/stylesheets/theme-light.css"> <link rel="stylesheet" href="<%= assetPath %>/stylesheets/theme-light.css">
<link rel="stylesheet" href="<%= assetPath %>/stylesheets/theme-next.css"> <link rel="stylesheet" href="<%= assetPath %>/stylesheets/theme-next.css">
<link rel="stylesheet" href="<%= assetPath %>/stylesheets/style.css"> <link rel="stylesheet" href="<%= assetPath %>/stylesheets/style.css">
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
</head> </head>
<body> <body lang="<%= currentLocale.id %>" dir="<%= currentLocale.rtl ? 'rtl' : 'ltr' %>">
<div class="container login-page"> <div class="container login-page">
<div class="col-xs-12 col-sm-10 col-md-6 col-lg-4 col-xl-4 mx-auto pt-4"> <div class="col-xs-12 col-sm-10 col-md-6 col-lg-4 col-xl-4 mx-auto pt-4">
<img class="img-fluid d-block mx-auto" style="height: 8rem;" src="<%= assetPathFragment %>/images/icon-color.svg" aria-hidden="true" draggable="false" > <img class="img-fluid d-block mx-auto" style="height: 8rem;" src="<%= assetPathFragment %>/images/icon-color.svg" aria-hidden="true" draggable="false" >
@@ -76,7 +75,6 @@
</div> </div>
</div> </div>
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
<script src="<%= appPath %>/login.js" crossorigin type="module"></script> <script src="<%= appPath %>/login.js" crossorigin type="module"></script>
</body> </body>

View File

@@ -96,8 +96,13 @@
} }
} }
</style> </style>
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
</head> </head>
<body class="mobile heading-style-<%= headingStyle %>"> <body
class="mobile heading-style-<%= headingStyle %>"
lang="<%= currentLocale.id %>" dir="<%= currentLocale.rtl ? 'rtl' : 'ltr' %>"
>
<noscript><%= t("javascript-required") %></noscript> <noscript><%= t("javascript-required") %></noscript>
<div id="toast-container" class="d-flex flex-column justify-content-center align-items-center"></div> <div id="toast-container" class="d-flex flex-column justify-content-center align-items-center"></div>
@@ -108,11 +113,9 @@
<%- include("./partials/windowGlobal.ejs", locals) %> <%- include("./partials/windowGlobal.ejs", locals) %>
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
<script src="<%= appPath %>/mobile.js" crossorigin type="module"></script> <script src="<%= appPath %>/mobile.js" crossorigin type="module"></script>
<link href="api/fonts" rel="stylesheet"> <link href="api/fonts" rel="stylesheet">
<link rel="stylesheet" href="<%= appPath %>/bootstrap.css">
<link href="<%= assetPath %>/stylesheets/ckeditor-theme.css" rel="stylesheet"> <link href="<%= assetPath %>/stylesheets/ckeditor-theme.css" rel="stylesheet">
<link href="<%= assetPath %>/stylesheets/theme-light.css" rel="stylesheet"> <link href="<%= assetPath %>/stylesheets/theme-light.css" rel="stylesheet">
<% if (themeCssUrl) { %> <% if (themeCssUrl) { %>

View File

@@ -18,6 +18,7 @@
appPath: "<%= appPath %>", appPath: "<%= appPath %>",
platform: "<%= platform %>", platform: "<%= platform %>",
hasNativeTitleBar: <%= hasNativeTitleBar %>, hasNativeTitleBar: <%= hasNativeTitleBar %>,
TRILIUM_SAFE_MODE: <%= !!process.env.TRILIUM_SAFE_MODE %> TRILIUM_SAFE_MODE: <%= !!process.env.TRILIUM_SAFE_MODE %>,
isRtl: <%= !!currentLocale.rtl %>
}; };
</script> </script>

View File

@@ -6,13 +6,18 @@
<title><%= t("set_password.title") %></title> <title><%= t("set_password.title") %></title>
<link rel="apple-touch-icon" sizes="180x180" href="<%= assetPath %>/images/app-icons/ios/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="<%= assetPath %>/images/app-icons/ios/apple-touch-icon.png">
<link rel="shortcut icon" href="favicon.ico"> <link rel="shortcut icon" href="favicon.ico">
<% // TriliumNextTODO: move the css file to ${assetPath}/stylesheets/ %>
<link rel="stylesheet" href="<%= appPath %>/bootstrap.css">
<link rel="stylesheet" href="<%= assetPath %>/stylesheets/theme-light.css"> <link rel="stylesheet" href="<%= assetPath %>/stylesheets/theme-light.css">
<link rel="stylesheet" href="<%= assetPath %>/stylesheets/theme-next.css"> <link rel="stylesheet" href="<%= assetPath %>/stylesheets/theme-next.css">
<link rel="stylesheet" href="<%= assetPath %>/stylesheets/style.css"> <link rel="stylesheet" href="<%= assetPath %>/stylesheets/style.css">
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
<style>
body {
/* Prevent the content from being rendered before the main stylesheet loads */
display: none;
}
</style>
</head> </head>
<body> <body lang="<%= currentLocale.id %>" dir="<%= currentLocale.rtl ? 'rtl' : 'ltr' %>">
<div class="container set-password"> <div class="container set-password">
<div class="col-xs-12 col-sm-10 col-md-6 col-lg-4 col-xl-4 mx-auto pt-4"> <div class="col-xs-12 col-sm-10 col-md-6 col-lg-4 col-xl-4 mx-auto pt-4">
<h1><%= t("set_password.heading") %></h1> <h1><%= t("set_password.heading") %></h1>
@@ -46,7 +51,6 @@
</div> </div>
</div> </div>
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
<script src="<%= appPath %>/set_password.js" crossorigin type="module"></script> <script src="<%= appPath %>/set_password.js" crossorigin type="module"></script>
</body> </body>

View File

@@ -6,10 +6,11 @@
<meta id="syncInProgress" content="<%= syncInProgress ? 1 : 0 %>" /> <meta id="syncInProgress" content="<%= syncInProgress ? 1 : 0 %>" />
<title><%= t("setup.title") %></title> <title><%= t("setup.title") %></title>
<% // TriliumNextTODO: move the css file to ${assetPath}/stylesheets/ %>
<link rel="stylesheet" href="<%= appPath %>/bootstrap.css">
<style> <style>
body {
/* Prevent the content from being rendered before the main stylesheet loads */
display: none;
}
.lds-ring { .lds-ring {
display: inline-block; display: inline-block;
position: relative; position: relative;
@@ -46,8 +47,9 @@
} }
} }
</style> </style>
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
</head> </head>
<body> <body lang="<%= currentLocale.id %>" dir="<%= currentLocale.rtl ? 'rtl' : 'ltr' %>">
<noscript><%= t("javascript-required") %></noscript> <noscript><%= t("javascript-required") %></noscript>
<div class="container"> <div class="container">
<div id="setup-dialog" class="col-md-12 col-lg-8 col-xl-6 mx-auto" style="padding-top: 25px; font-size: larger; display: none;"> <div id="setup-dialog" class="col-md-12 col-lg-8 col-xl-6 mx-auto" style="padding-top: 25px; font-size: larger; display: none;">
@@ -168,7 +170,6 @@
<!-- Required for correct loading of scripts in Electron --> <!-- Required for correct loading of scripts in Electron -->
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script> <script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<script src="<%= appPath %>/runtime.js" crossorigin type="module"></script>
<script src="<%= appPath %>/setup.js" crossorigin type="module"></script> <script src="<%= appPath %>/setup.js" crossorigin type="module"></script>
<link href="<%= assetPath %>/stylesheets/theme-light.css" rel="stylesheet" /> <link href="<%= assetPath %>/stylesheets/theme-light.css" rel="stylesheet" />
<link href="<%= assetPath %>/stylesheets/theme-next.css" rel="stylesheet" /> <link href="<%= assetPath %>/stylesheets/theme-next.css" rel="stylesheet" />

View File

@@ -14,6 +14,7 @@ import { generateToken as generateCsrfToken } from "./csrf_protection.js";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import type BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
import { getCurrentLocale } from "../services/i18n.js";
function index(req: Request, res: Response) { function index(req: Request, res: Response) {
const options = optionService.getOptionMap(); const options = optionService.getOptionMap();
@@ -57,7 +58,8 @@ function index(req: Request, res: Response) {
maxContentWidth: Math.max(640, parseInt(options.maxContentWidth)), maxContentWidth: Math.max(640, parseInt(options.maxContentWidth)),
triliumVersion: packageJson.version, triliumVersion: packageJson.version,
assetPath: assetPath, assetPath: assetPath,
appPath: appPath appPath: appPath,
currentLocale: getCurrentLocale()
}); });
} }

View File

@@ -11,9 +11,10 @@ import totp from '../services/totp.js';
import recoveryCodeService from '../services/encryption/recovery_codes.js'; import recoveryCodeService from '../services/encryption/recovery_codes.js';
import openID from '../services/open_id.js'; import openID from '../services/open_id.js';
import openIDEncryption from '../services/encryption/open_id_encryption.js'; import openIDEncryption from '../services/encryption/open_id_encryption.js';
import { getCurrentLocale } from "../services/i18n.js";
function loginPage(req: Request, res: Response) { function loginPage(req: Request, res: Response) {
// Login page is triggered twice. Once here, and another time if the password is failed. // Login page is triggered twice. Once here, and another time (see sendLoginError) if the password is failed.
res.render('login', { res.render('login', {
wrongPassword: false, wrongPassword: false,
wrongTotp: false, wrongTotp: false,
@@ -24,6 +25,7 @@ function loginPage(req: Request, res: Response) {
assetPath: assetPath, assetPath: assetPath,
assetPathFragment: assetUrlFragment, assetPathFragment: assetUrlFragment,
appPath: appPath, appPath: appPath,
currentLocale: getCurrentLocale()
}); });
} }
@@ -31,7 +33,8 @@ function setPasswordPage(req: Request, res: Response) {
res.render("set_password", { res.render("set_password", {
error: false, error: false,
assetPath, assetPath,
appPath appPath,
currentLocale: getCurrentLocale()
}); });
} }
@@ -56,7 +59,8 @@ function setPassword(req: Request, res: Response) {
res.render("set_password", { res.render("set_password", {
error, error,
assetPath, assetPath,
appPath appPath,
currentLocale: getCurrentLocale()
}); });
return; return;
} }
@@ -175,6 +179,7 @@ function sendLoginError(req: Request, res: Response, errorType: 'password' | 'to
assetPath: assetPath, assetPath: assetPath,
assetPathFragment: assetUrlFragment, assetPathFragment: assetUrlFragment,
appPath: appPath, appPath: appPath,
currentLocale: getCurrentLocale()
}); });
} }

View File

@@ -6,6 +6,7 @@ import { isElectron } from "../services/utils.js";
import assetPath from "../services/asset_path.js"; import assetPath from "../services/asset_path.js";
import appPath from "../services/app_path.js"; import appPath from "../services/app_path.js";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import { getCurrentLocale } from "../services/i18n.js";
function setupPage(req: Request, res: Response) { function setupPage(req: Request, res: Response) {
if (sqlInit.isDbInitialized()) { if (sqlInit.isDbInitialized()) {
@@ -30,7 +31,8 @@ function setupPage(req: Request, res: Response) {
res.render("setup", { res.render("setup", {
syncInProgress: syncInProgress, syncInProgress: syncInProgress,
assetPath: assetPath, assetPath: assetPath,
appPath: appPath appPath: appPath,
currentLocale: getCurrentLocale()
}); });
} }
@@ -39,7 +41,7 @@ async function handleElectronRedirect() {
const { app } = await import("electron"); const { app } = await import("electron");
// Wait for the main window to be created before closing the setup window to prevent triggering `window-all-closed`. // Wait for the main window to be created before closing the setup window to prevent triggering `window-all-closed`.
await windowService.createMainWindow(app); await windowService.createMainWindow(app);
windowService.closeSetupWindow(); windowService.closeSetupWindow();
const tray = (await import("../services/tray.js")).default; const tray = (await import("../services/tray.js")).default;

View File

@@ -6,7 +6,7 @@ import { DAYJS_LOADER } from "./i18n";
describe("i18n", () => { describe("i18n", () => {
it("translations are valid JSON", () => { it("translations are valid JSON", () => {
for (const locale of LOCALES) { for (const locale of LOCALES) {
if (locale.contentOnly) { if (locale.contentOnly || locale.id === "en_rtl") {
continue; continue;
} }

View File

@@ -12,6 +12,7 @@ export const DAYJS_LOADER: Record<LOCALE_IDS, () => Promise<typeof import("dayjs
"cn": () => import("dayjs/locale/zh-cn.js"), "cn": () => import("dayjs/locale/zh-cn.js"),
"de": () => import("dayjs/locale/de.js"), "de": () => import("dayjs/locale/de.js"),
"en": () => import("dayjs/locale/en.js"), "en": () => import("dayjs/locale/en.js"),
"en_rtl": () => import("dayjs/locale/en.js"),
"es": () => import("dayjs/locale/es.js"), "es": () => import("dayjs/locale/es.js"),
"fa": () => import("dayjs/locale/fa.js"), "fa": () => import("dayjs/locale/fa.js"),
"fr": () => import("dayjs/locale/fr.js"), "fr": () => import("dayjs/locale/fr.js"),
@@ -75,3 +76,10 @@ export async function changeLanguage(locale: string) {
await i18next.changeLanguage(locale); await i18next.changeLanguage(locale);
hidden_subtree.checkHiddenSubtree(true, { restoreNames: true }); hidden_subtree.checkHiddenSubtree(true, { restoreNames: true });
} }
export function getCurrentLocale() {
const localeId = options.getOptionOrNull("locale") ?? "en";
const currentLocale = LOCALES.find(l => l.id === localeId);
if (!currentLocale) return LOCALES.find(l => l.id === "en")!;
return currentLocale;
}

View File

@@ -159,7 +159,7 @@ const defaultOptions: DefaultOption[] = [
// Internationalization // Internationalization
{ name: "locale", value: "en", isSynced: true }, { name: "locale", value: "en", isSynced: true },
{ name: "formattingLocale", value: "en", isSynced: true }, { name: "formattingLocale", value: "", isSynced: true }, // no value means auto-detect
{ name: "firstDayOfWeek", value: "1", isSynced: true }, { name: "firstDayOfWeek", value: "1", isSynced: true },
{ name: "firstWeekOfYear", value: "0", isSynced: true }, { name: "firstWeekOfYear", value: "0", isSynced: true },
{ name: "minDaysInFirstWeek", value: "4", isSynced: true }, { name: "minDaysInFirstWeek", value: "4", isSynced: true },

View File

@@ -5,11 +5,13 @@ export interface Locale {
rtl?: boolean; rtl?: boolean;
/** `true` if the language is not supported by the application as a display language, but it is selectable by the user for the content. */ /** `true` if the language is not supported by the application as a display language, but it is selectable by the user for the content. */
contentOnly?: boolean; contentOnly?: boolean;
/** `true` if the language should only be visible while in development mode, and not in production. */
devOnly?: boolean;
/** The value to pass to `--lang` for the Electron instance in order to set it as a locale. Not setting it will hide it from the list of supported locales. */ /** The value to pass to `--lang` for the Electron instance in order to set it as a locale. Not setting it will hide it from the list of supported locales. */
electronLocale?: "en" | "de" | "es" | "fr" | "zh_CN" | "zh_TW" | "ro" | "af" | "am" | "ar" | "bg" | "bn" | "ca" | "cs" | "da" | "el" | "en_GB" | "es_419" | "et" | "fa" | "fi" | "fil" | "gu" | "he" | "hi" | "hr" | "hu" | "id" | "it" | "ja" | "kn" | "ko" | "lt" | "lv" | "ml" | "mr" | "ms" | "nb" | "nl" | "pl" | "pt_BR" | "pt_PT" | "ru" | "sk" | "sl" | "sr" | "sv" | "sw" | "ta" | "te" | "th" | "tr" | "uk" | "ur" | "vi"; electronLocale?: "en" | "de" | "es" | "fr" | "zh_CN" | "zh_TW" | "ro" | "af" | "am" | "ar" | "bg" | "bn" | "ca" | "cs" | "da" | "el" | "en_GB" | "es_419" | "et" | "fa" | "fi" | "fil" | "gu" | "he" | "hi" | "hr" | "hu" | "id" | "it" | "ja" | "kn" | "ko" | "lt" | "lv" | "ml" | "mr" | "ms" | "nb" | "nl" | "pl" | "pt_BR" | "pt_PT" | "ru" | "sk" | "sl" | "sr" | "sv" | "sw" | "ta" | "te" | "th" | "tr" | "uk" | "ur" | "vi";
} }
const UNSORTED_LOCALES: Locale[] = [ const UNSORTED_LOCALES = [
{ id: "cn", name: "简体中文", electronLocale: "zh_CN" }, { id: "cn", name: "简体中文", electronLocale: "zh_CN" },
{ id: "de", name: "Deutsch", electronLocale: "de" }, { id: "de", name: "Deutsch", electronLocale: "de" },
{ id: "en", name: "English", electronLocale: "en" }, { id: "en", name: "English", electronLocale: "en" },
@@ -23,6 +25,19 @@ const UNSORTED_LOCALES: Locale[] = [
{ id: "tw", name: "繁體中文", electronLocale: "zh_TW" }, { id: "tw", name: "繁體中文", electronLocale: "zh_TW" },
{ id: "uk", name: "Українська", electronLocale: "uk" }, { id: "uk", name: "Українська", electronLocale: "uk" },
/**
* Development-only languages.
*
* These are only displayed while in dev mode, to test some language particularities (such as RTL) more easily.
*/
{
id: "en_rtl",
name: "English (right-to-left) [dev]",
electronLocale: "en",
rtl: true,
devOnly: true
},
/* /*
* Right to left languages * Right to left languages
* *
@@ -32,7 +47,8 @@ const UNSORTED_LOCALES: Locale[] = [
id: "ar", id: "ar",
name: "اَلْعَرَبِيَّةُ", name: "اَلْعَرَبِيَّةُ",
rtl: true, rtl: true,
contentOnly: true devOnly: true,
electronLocale: "ar"
}, },
{ // Hebrew { // Hebrew
id: "he", id: "he",
@@ -57,4 +73,7 @@ const UNSORTED_LOCALES: Locale[] = [
export const LOCALES: Locale[] = Array.from(UNSORTED_LOCALES) export const LOCALES: Locale[] = Array.from(UNSORTED_LOCALES)
.sort((a, b) => a.name.localeCompare(b.name)); .sort((a, b) => a.name.localeCompare(b.name));
/** A type containing a string union of all the supported locales, including those that are content-only. */
export type LOCALE_IDS = typeof UNSORTED_LOCALES[number]["id"]; export type LOCALE_IDS = typeof UNSORTED_LOCALES[number]["id"];
/** A type containing a string union of all the supported locales that are not content-only (i.e. can be used as the UI language). */
export type DISPLAYABLE_LOCALE_IDS = Exclude<typeof UNSORTED_LOCALES[number], { contentOnly: true }>["id"];