mirror of
https://github.com/zadam/trilium.git
synced 2025-11-30 04:24:24 +01:00
Merge branch 'main' of https://github.com/TriliumNext/Trilium
This commit is contained in:
commit
1346ffb77e
@ -487,7 +487,7 @@ type EventMappings = {
|
|||||||
relationMapResetPanZoom: { ntxId: string | null | undefined };
|
relationMapResetPanZoom: { ntxId: string | null | undefined };
|
||||||
relationMapResetZoomIn: { ntxId: string | null | undefined };
|
relationMapResetZoomIn: { ntxId: string | null | undefined };
|
||||||
relationMapResetZoomOut: { ntxId: string | null | undefined };
|
relationMapResetZoomOut: { ntxId: string | null | undefined };
|
||||||
activeNoteChanged: {};
|
activeNoteChanged: {ntxId: string | null | undefined};
|
||||||
showAddLinkDialog: AddLinkOpts;
|
showAddLinkDialog: AddLinkOpts;
|
||||||
showIncludeDialog: IncludeNoteOpts;
|
showIncludeDialog: IncludeNoteOpts;
|
||||||
openBulkActionsDialog: {
|
openBulkActionsDialog: {
|
||||||
|
|||||||
@ -165,7 +165,7 @@ export default class TabManager extends Component {
|
|||||||
const activeNoteContext = this.getActiveContext();
|
const activeNoteContext = this.getActiveContext();
|
||||||
this.updateDocumentTitle(activeNoteContext);
|
this.updateDocumentTitle(activeNoteContext);
|
||||||
|
|
||||||
this.triggerEvent("activeNoteChanged", {}); // trigger this even in on popstate event
|
this.triggerEvent("activeNoteChanged", {ntxId:activeNoteContext?.ntxId}); // trigger this even in on popstate event
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateHash(): string {
|
calculateHash(): string {
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import PromotedAttributes from "../widgets/PromotedAttributes.jsx";
|
|||||||
|
|
||||||
const MOBILE_CSS = `
|
const MOBILE_CSS = `
|
||||||
<style>
|
<style>
|
||||||
|
span.keyboard-shortcut,
|
||||||
kbd {
|
kbd {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
import clsx from "clsx";
|
||||||
import {readCssVar} from "../utils/css-var";
|
import {readCssVar} from "../utils/css-var";
|
||||||
import Color, { ColorInstance } from "color";
|
import Color, { ColorInstance } from "color";
|
||||||
|
|
||||||
const registeredClasses = new Set<string>();
|
const registeredClasses = new Set<string>();
|
||||||
|
const colorsWithHue = new Set<string>();
|
||||||
|
|
||||||
// Read the color lightness limits defined in the theme as CSS variables
|
// Read the color lightness limits defined in the theme as CSS variables
|
||||||
|
|
||||||
@ -26,19 +28,23 @@ function createClassForColor(colorString: string | null) {
|
|||||||
if (!registeredClasses.has(className)) {
|
if (!registeredClasses.has(className)) {
|
||||||
const adjustedColor = adjustColorLightness(color, lightThemeColorMaxLightness!,
|
const adjustedColor = adjustColorLightness(color, lightThemeColorMaxLightness!,
|
||||||
darkThemeColorMinLightness!);
|
darkThemeColorMinLightness!);
|
||||||
|
const hue = getHue(color);
|
||||||
|
|
||||||
$("head").append(`<style>
|
$("head").append(`<style>
|
||||||
.${className}, span.fancytree-active.${className} {
|
.${className}, span.fancytree-active.${className} {
|
||||||
--light-theme-custom-color: ${adjustedColor.lightThemeColor};
|
--light-theme-custom-color: ${adjustedColor.lightThemeColor};
|
||||||
--dark-theme-custom-color: ${adjustedColor.darkThemeColor};
|
--dark-theme-custom-color: ${adjustedColor.darkThemeColor};
|
||||||
--custom-color-hue: ${getHue(color) ?? 'unset'};
|
--custom-color-hue: ${hue ?? 'unset'};
|
||||||
}
|
}
|
||||||
</style>`);
|
</style>`);
|
||||||
|
|
||||||
registeredClasses.add(className);
|
registeredClasses.add(className);
|
||||||
|
if (hue) {
|
||||||
|
colorsWithHue.add(className);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return `color ${className}`;
|
return clsx(className, colorsWithHue.has(className) && "with-hue");
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseColor(color: string) {
|
function parseColor(color: string) {
|
||||||
|
|||||||
@ -98,6 +98,7 @@
|
|||||||
--menu-item-delimiter-color: #ffffff1c;
|
--menu-item-delimiter-color: #ffffff1c;
|
||||||
--menu-item-group-header-color: #ffffff91;
|
--menu-item-group-header-color: #ffffff91;
|
||||||
--menu-section-background-color: #fefefe08;
|
--menu-section-background-color: #fefefe08;
|
||||||
|
--menu-submenu-mobile-background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
|
||||||
--modal-backdrop-color: #000;
|
--modal-backdrop-color: #000;
|
||||||
--modal-shadow-color: rgba(0, 0, 0, .5);
|
--modal-shadow-color: rgba(0, 0, 0, .5);
|
||||||
@ -300,7 +301,7 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
|
|||||||
border-color: var(--muted-text-color) !important;
|
border-color: var(--muted-text-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quick-edit-dialog-wrapper.color {
|
.quick-edit-dialog-wrapper.with-hue {
|
||||||
--modal-background-color: hsl(var(--custom-color-hue), 8.8%, 11.2%);
|
--modal-background-color: hsl(var(--custom-color-hue), 8.8%, 11.2%);
|
||||||
--modal-border-color: hsl(var(--custom-color-hue), 9.4%, 25.1%);
|
--modal-border-color: hsl(var(--custom-color-hue), 9.4%, 25.1%);
|
||||||
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 13.2%, 20.8%);
|
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 13.2%, 20.8%);
|
||||||
|
|||||||
@ -276,7 +276,7 @@
|
|||||||
--custom-bg-color: hsl(var(--custom-color-hue), 37%, 89%, 1);
|
--custom-bg-color: hsl(var(--custom-color-hue), 37%, 89%, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.quick-edit-dialog-wrapper.color {
|
.quick-edit-dialog-wrapper.with-hue {
|
||||||
--modal-background-color: hsl(var(--custom-color-hue), 56%, 96%);
|
--modal-background-color: hsl(var(--custom-color-hue), 56%, 96%);
|
||||||
--modal-border-color: hsl(var(--custom-color-hue), 33%, 41%);
|
--modal-border-color: hsl(var(--custom-color-hue), 33%, 41%);
|
||||||
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 40%, 88%);
|
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 40%, 88%);
|
||||||
|
|||||||
@ -62,6 +62,7 @@
|
|||||||
|
|
||||||
--menu-padding-size: 8px;
|
--menu-padding-size: 8px;
|
||||||
--menu-item-icon-vert-offset: -2px;
|
--menu-item-icon-vert-offset: -2px;
|
||||||
|
--menu-submenu-mobile-background-color: rgba(255, 255, 255, 0.15);
|
||||||
|
|
||||||
--more-accented-background-color: var(--card-background-hover-color);
|
--more-accented-background-color: var(--card-background-hover-color);
|
||||||
|
|
||||||
@ -384,7 +385,8 @@ body.mobile .dropdown-menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
--menu-background-color: rgba(0, 0, 0, 0.15);
|
--menu-background-color: --menu-submenu-mobile-background-color;
|
||||||
|
--bs-dropdown-divider-margin-y: 0.25rem;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
transition: max-height 100ms ease-in;
|
transition: max-height 100ms ease-in;
|
||||||
@ -392,22 +394,7 @@ body.mobile .dropdown-menu {
|
|||||||
|
|
||||||
&.show {
|
&.show {
|
||||||
max-height: 1000px;
|
max-height: 1000px;
|
||||||
}
|
padding: 0.5rem 0.75rem !important;
|
||||||
|
|
||||||
.dropdown-item {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-divider {
|
|
||||||
visibility: visible;
|
|
||||||
margin: 0;
|
|
||||||
height: 3px;
|
|
||||||
border-top: unset;
|
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: unset;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -105,9 +105,11 @@ export default function NoteDetail() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Automatically focus the editor.
|
// Automatically focus the editor.
|
||||||
useTriliumEvent("activeNoteChanged", () => {
|
useTriliumEvent("activeNoteChanged", ({ ntxId: eventNtxId }) => {
|
||||||
// Restore focus to the editor when switching tabs, but only if the note tree is not already focused.
|
if (eventNtxId != ntxId) return;
|
||||||
if (!document.activeElement?.classList.contains("fancytree-title")) {
|
// Restore focus to the editor when switching tabs,
|
||||||
|
// but only if the note tree and the note panel (e.g., note title or note detail) are not focused.
|
||||||
|
if (!document.activeElement?.classList.contains("fancytree-title") && !parentComponent.$widget[0].closest(".note-split")?.contains(document.activeElement)) {
|
||||||
parentComponent.triggerCommand("focusOnDetail", { ntxId });
|
parentComponent.triggerCommand("focusOnDetail", { ntxId });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -29,7 +29,11 @@ export default class LeftPaneContainer extends FlexContainer<Component> {
|
|||||||
if (visible) {
|
if (visible) {
|
||||||
this.triggerEvent("focusTree", {});
|
this.triggerEvent("focusTree", {});
|
||||||
} else {
|
} else {
|
||||||
this.triggerEvent("focusOnDetail", { ntxId: appContext.tabManager.getActiveContext()?.ntxId });
|
const ntxId = appContext.tabManager.getActiveContext()?.ntxId;
|
||||||
|
const noteContainer = document.querySelector(`.note-split[data-ntx-id="${ntxId}"]`);
|
||||||
|
if (!noteContainer?.contains(document.activeElement)) {
|
||||||
|
this.triggerEvent("focusOnDetail", { ntxId });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options.save("leftPaneVisible", this.currentLeftPaneVisible.toString());
|
options.save("leftPaneVisible", this.currentLeftPaneVisible.toString());
|
||||||
|
|||||||
@ -14,11 +14,15 @@ body.desktop .modal.popup-editor-dialog .modal-dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.mobile .modal.popup-editor-dialog .modal-dialog {
|
body.mobile .modal.popup-editor-dialog .modal-dialog {
|
||||||
max-width: 90vw;
|
max-width: min(var(--preferred-max-content-width), 95vw);
|
||||||
max-height: var(--tn-modal-max-height);
|
max-height: var(--tn-modal-max-height);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal.popup-editor-dialog .modal-content {
|
||||||
|
transition: background-color 250ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
.modal.popup-editor-dialog .modal-header .modal-title {
|
.modal.popup-editor-dialog .modal-header .modal-title {
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
@ -58,12 +62,16 @@ body.mobile .modal.popup-editor-dialog .modal-dialog {
|
|||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal.popup-editor-dialog .classic-toolbar-outer-container.visible {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.modal.popup-editor-dialog .classic-toolbar-widget {
|
.modal.popup-editor-dialog .classic-toolbar-widget {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
inset-inline-start: 0;
|
inset-inline-start: 0;
|
||||||
inset-inline-end: 0;
|
inset-inline-end: 0;
|
||||||
background: var(--modal-background-color);
|
background: transparent;
|
||||||
z-index: 998;
|
z-index: 998;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,16 +10,16 @@ import FNote from "../../entities/fnote";
|
|||||||
import search from "../../services/search";
|
import search from "../../services/search";
|
||||||
import { TypeWidgetProps } from "./type_widget";
|
import { TypeWidgetProps } from "./type_widget";
|
||||||
|
|
||||||
export default function Empty({ }: TypeWidgetProps) {
|
export default function Empty({ ntxId }: TypeWidgetProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WorkspaceSwitcher />
|
<WorkspaceSwitcher />
|
||||||
<NoteSearch />
|
<NoteSearch ntxId={ntxId ?? null} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function NoteSearch() {
|
function NoteSearch({ ntxId }: { ntxId: string | null }) {
|
||||||
const resultsContainerRef = useRef<HTMLDivElement>(null);
|
const resultsContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const autocompleteRef = useRef<HTMLInputElement>(null);
|
const autocompleteRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@ -45,10 +45,9 @@ function NoteSearch() {
|
|||||||
if (!suggestion?.notePath) {
|
if (!suggestion?.notePath) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const activeNoteContext = appContext.tabManager.getNoteContextById(ntxId) ?? appContext.tabManager.getActiveContext();
|
||||||
const activeContext = appContext.tabManager.getActiveContext();
|
if (activeNoteContext) {
|
||||||
if (activeContext) {
|
activeNoteContext.setNote(suggestion.notePath);
|
||||||
activeContext.setNote(suggestion.notePath);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -98,6 +98,14 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
|
|||||||
editorApi: editorApiRef.current,
|
editorApi: editorApiRef.current,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
insertDateTimeToTextCommand() {
|
||||||
|
if (!editorApiRef.current) return;
|
||||||
|
const date = new Date();
|
||||||
|
const customDateTimeFormat = options.get("customDateTimeFormat");
|
||||||
|
const dateString = utils.formatDateTime(date, customDateTimeFormat);
|
||||||
|
|
||||||
|
addTextToEditor(dateString);
|
||||||
|
},
|
||||||
// Include note functionality note
|
// Include note functionality note
|
||||||
addIncludeNoteToTextCommand() {
|
addIncludeNoteToTextCommand() {
|
||||||
if (!editorApiRef.current) return;
|
if (!editorApiRef.current) return;
|
||||||
@ -197,14 +205,6 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useTriliumEvent("insertDateTimeToText", ({ ntxId: eventNtxId }) => {
|
|
||||||
if (eventNtxId !== ntxId) return;
|
|
||||||
const date = new Date();
|
|
||||||
const customDateTimeFormat = options.get("customDateTimeFormat");
|
|
||||||
const dateString = utils.formatDateTime(date, customDateTimeFormat);
|
|
||||||
|
|
||||||
addTextToEditor(dateString);
|
|
||||||
});
|
|
||||||
useTriliumEvent("addTextToActiveEditor", ({ text }) => {
|
useTriliumEvent("addTextToActiveEditor", ({ text }) => {
|
||||||
if (!noteContext?.isActive()) return;
|
if (!noteContext?.isActive()) return;
|
||||||
addTextToEditor(text);
|
addTextToEditor(text);
|
||||||
|
|||||||
72
apps/server-e2e/src/layout/split_pane.spec.ts
Normal file
72
apps/server-e2e/src/layout/split_pane.spec.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
import App from "../support/app";
|
||||||
|
|
||||||
|
const TEXT_NOTE_TITLE = "Text notes";
|
||||||
|
const CODE_NOTE_TITLE = "Code notes";
|
||||||
|
|
||||||
|
test("Open the note in the correct split pane", async ({ page, context }) => {
|
||||||
|
const app = new App(page, context);
|
||||||
|
await app.goto();
|
||||||
|
await app.closeAllTabs();
|
||||||
|
|
||||||
|
// Open the first split.
|
||||||
|
await app.goToNoteInNewTab(TEXT_NOTE_TITLE);
|
||||||
|
const split1 = app.currentNoteSplit;
|
||||||
|
|
||||||
|
// Create a new split.
|
||||||
|
const splitButton = split1.locator("button.bx-dock-right");
|
||||||
|
await expect(splitButton).toBeVisible();
|
||||||
|
await splitButton.click();
|
||||||
|
|
||||||
|
// Search for "Code notes" in the empty area of the second split.
|
||||||
|
const split2 = app.currentNoteSplit.nth(1);;
|
||||||
|
await expect(split2).toBeVisible();
|
||||||
|
const autocomplete = split2.locator(".note-autocomplete");
|
||||||
|
await autocomplete.fill(CODE_NOTE_TITLE);
|
||||||
|
const resultsSelector = split2.locator(".note-detail-empty-results");
|
||||||
|
await expect(resultsSelector).toContainText(CODE_NOTE_TITLE);
|
||||||
|
|
||||||
|
//Focus on the first split.
|
||||||
|
const noteContent = split1.locator(".note-detail-editable-text-editor");
|
||||||
|
await expect(noteContent.locator("p")).toBeVisible();
|
||||||
|
await noteContent.focus();
|
||||||
|
|
||||||
|
// Click the search result in the second split.
|
||||||
|
await resultsSelector.locator(".aa-suggestion", { hasText: CODE_NOTE_TITLE })
|
||||||
|
.nth(1).click();
|
||||||
|
|
||||||
|
await expect(split2).toContainText(CODE_NOTE_TITLE);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Can directly focus the autocomplete input within the split", async ({ page, context }) => {
|
||||||
|
const app = new App(page, context);
|
||||||
|
await app.goto();
|
||||||
|
await app.closeAllTabs();
|
||||||
|
|
||||||
|
// Open the first split.
|
||||||
|
await app.goToNoteInNewTab(TEXT_NOTE_TITLE);
|
||||||
|
const split1 = app.currentNoteSplit;
|
||||||
|
|
||||||
|
// Create a new split.
|
||||||
|
const splitButton = split1.locator("button.bx-dock-right");
|
||||||
|
await expect(splitButton).toBeVisible();
|
||||||
|
await splitButton.click();
|
||||||
|
|
||||||
|
// Search for "Code notes" in the empty area of the second split.
|
||||||
|
const split2 = app.currentNoteSplit.nth(1);;
|
||||||
|
await expect(split2).toBeVisible();
|
||||||
|
|
||||||
|
// Focus the first split.
|
||||||
|
const noteContent = split1.locator(".note-detail-editable-text-editor");
|
||||||
|
await expect(noteContent.locator("p")).toBeVisible();
|
||||||
|
await noteContent.focus();
|
||||||
|
await noteContent.click();
|
||||||
|
|
||||||
|
// click the autocomplete input box of the second split
|
||||||
|
const autocomplete = split2.locator(".note-autocomplete");
|
||||||
|
await autocomplete.focus();
|
||||||
|
await autocomplete.click();
|
||||||
|
|
||||||
|
await page.waitForTimeout(100);
|
||||||
|
await expect(autocomplete).toBeFocused();
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user