Mobile improvements v2 (#8615)
@@ -46,10 +46,6 @@ if (utils.isElectron()) {
|
||||
electronContextMenu.setupContextMenu();
|
||||
}
|
||||
|
||||
if (utils.isPWA()) {
|
||||
initPWATopbarColor();
|
||||
}
|
||||
|
||||
function initOnElectron() {
|
||||
const electron: typeof Electron = utils.dynamicRequire("electron");
|
||||
electron.ipcRenderer.on("globalShortcut", async (event, actionName) => appContext.triggerCommand(actionName));
|
||||
@@ -134,20 +130,3 @@ function initDarkOrLightMode(style: CSSStyleDeclaration) {
|
||||
const { nativeTheme } = utils.dynamicRequire("@electron/remote") as typeof ElectronRemote;
|
||||
nativeTheme.themeSource = themeSource;
|
||||
}
|
||||
|
||||
function initPWATopbarColor() {
|
||||
const tracker = $("#background-color-tracker");
|
||||
|
||||
if (tracker.length) {
|
||||
const applyThemeColor = () => {
|
||||
let meta = $("meta[name='theme-color']");
|
||||
if (!meta.length) {
|
||||
meta = $(`<meta name="theme-color">`).appendTo($("head"));
|
||||
}
|
||||
meta.attr("content", tracker.css("color"));
|
||||
};
|
||||
|
||||
tracker.on("transitionend", applyThemeColor);
|
||||
applyThemeColor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#background-color-tracker {
|
||||
color: var(--main-background-color) !important;
|
||||
}
|
||||
|
||||
span.keyboard-shortcut,
|
||||
kbd {
|
||||
display: none;
|
||||
|
||||
@@ -77,7 +77,6 @@ export default class MobileLayout {
|
||||
.child(<NoteTitleActions />)
|
||||
.child(<NoteDetail />)
|
||||
.child(<NoteList media="screen" />)
|
||||
.child(<StandaloneRibbonAdapter component={SearchDefinitionTab} />)
|
||||
.child(<SearchResult />)
|
||||
.child(<FilePropertiesWrapper />)
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import treeService from "../services/tree.js";
|
||||
import froca from "../services/froca.js";
|
||||
import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js";
|
||||
import dialogService from "../services/dialog.js";
|
||||
import server from "../services/server.js";
|
||||
import { t } from "../services/i18n.js";
|
||||
import type { ContextMenuCommandData,FilteredCommandNames } from "../components/app_context.js";
|
||||
import type { SelectMenuItemEventListener } from "../components/events.js";
|
||||
import dialogService from "../services/dialog.js";
|
||||
import froca from "../services/froca.js";
|
||||
import { t } from "../services/i18n.js";
|
||||
import server from "../services/server.js";
|
||||
import treeService from "../services/tree.js";
|
||||
import type NoteTreeWidget from "../widgets/note_tree.js";
|
||||
import type { FilteredCommandNames, ContextMenuCommandData } from "../components/app_context.js";
|
||||
import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js";
|
||||
|
||||
type LauncherCommandNames = FilteredCommandNames<ContextMenuCommandData>;
|
||||
|
||||
@@ -32,8 +32,8 @@ export default class LauncherContextMenu implements SelectMenuItemEventListener<
|
||||
const note = this.node.data.noteId ? await froca.getNote(this.node.data.noteId) : null;
|
||||
const parentNoteId = this.node.getParent().data.noteId;
|
||||
|
||||
const isVisibleRoot = note?.noteId === "_lbVisibleLaunchers";
|
||||
const isAvailableRoot = note?.noteId === "_lbAvailableLaunchers";
|
||||
const isVisibleRoot = note?.noteId === "_lbVisibleLaunchers" || note?.noteId === "_lbMobileVisibleLaunchers";
|
||||
const isAvailableRoot = note?.noteId === "_lbAvailableLaunchers" || note?.noteId === "_lbMobileAvailableLaunchers";
|
||||
const isVisibleItem = parentNoteId === "_lbVisibleLaunchers" || parentNoteId === "_lbMobileVisibleLaunchers";
|
||||
const isAvailableItem = parentNoteId === "_lbAvailableLaunchers" || parentNoteId === "_lbMobileAvailableLaunchers";
|
||||
const isItem = isVisibleItem || isAvailableItem;
|
||||
|
||||
@@ -410,6 +410,7 @@ body.desktop .tabulator-popup-container,
|
||||
|
||||
.dropdown-menu.static {
|
||||
box-shadow: unset;
|
||||
backdrop-filter: unset !important;
|
||||
}
|
||||
|
||||
.dropend .dropdown-toggle::after {
|
||||
@@ -1553,8 +1554,14 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
||||
max-height: calc(var(--tn-modal-max-height) - var(--dropdown-bottom));
|
||||
}
|
||||
|
||||
body.mobile #launcher-container .dropdown > .dropdown-menu.show {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
body.mobile .dropdown-menu.mobile-bottom-menu.show {
|
||||
--dropdown-bottom: 0px;
|
||||
padding-bottom: calc(max(var(--menu-padding-size), env(safe-area-inset-bottom))) !important;
|
||||
}
|
||||
|
||||
#mobile-sidebar-container {
|
||||
|
||||
@@ -57,12 +57,12 @@
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* SEARCH PAGE
|
||||
*/
|
||||
|
||||
/* Button bar */
|
||||
.search-definition-widget .search-setting-table tbody:last-child div {
|
||||
.search-definition-widget .search-setting-table .search-actions-container {
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
@@ -143,7 +143,7 @@
|
||||
/*
|
||||
* OPTIONS PAGES
|
||||
*/
|
||||
|
||||
|
||||
:root {
|
||||
--options-card-min-width: 500px;
|
||||
--options-card-max-width: 900px;
|
||||
@@ -335,4 +335,4 @@ nav.options-section-tabs + .options-section {
|
||||
|
||||
.etapi-options-section div {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,7 +662,8 @@
|
||||
"show-cheatsheet": "Show Cheatsheet",
|
||||
"toggle-zen-mode": "Zen Mode",
|
||||
"new-version-available": "New Update Available",
|
||||
"download-update": "Get Version {{latestVersion}}"
|
||||
"download-update": "Get Version {{latestVersion}}",
|
||||
"search_notes": "Search notes"
|
||||
},
|
||||
"zen_mode": {
|
||||
"button_exit": "Exit Zen Mode"
|
||||
@@ -761,7 +762,8 @@
|
||||
"note_revisions": "Note revisions",
|
||||
"error_cannot_get_branch_id": "Cannot get branchId for notePath '{{notePath}}'",
|
||||
"error_unrecognized_command": "Unrecognized command {{command}}",
|
||||
"backlinks": "Backlinks"
|
||||
"backlinks": "Backlinks",
|
||||
"content_language_switcher": "Content language: {{language}}"
|
||||
},
|
||||
"note_icon": {
|
||||
"change_note_icon": "Change note icon",
|
||||
@@ -906,6 +908,7 @@
|
||||
"debug": "debug",
|
||||
"debug_description": "Debug will print extra debugging information into the console to aid in debugging complex queries",
|
||||
"action": "action",
|
||||
"option": "option",
|
||||
"search_button": "Search",
|
||||
"search_execute": "Search & Execute actions",
|
||||
"save_to_note": "Save to note",
|
||||
@@ -2277,5 +2280,8 @@
|
||||
"title_one": "{{count}} tab",
|
||||
"title_other": "{{count}} tabs",
|
||||
"more_options": "More options"
|
||||
},
|
||||
"bookmark_buttons": {
|
||||
"bookmarks": "Bookmarks"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
.fixed-note-tree-container {
|
||||
height: 60%;
|
||||
border-bottom: 1px solid var(--main-border-color);
|
||||
overflow: auto;
|
||||
|
||||
.tree-wrapper {
|
||||
padding: 0;
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function NoteDetail() {
|
||||
const [ noteTypesToRender, setNoteTypesToRender ] = useState<{ [ key in ExtendedNoteType ]?: (props: TypeWidgetProps) => VNode }>({});
|
||||
const [ activeNoteType, setActiveNoteType ] = useState<ExtendedNoteType>();
|
||||
const widgetRequestId = useRef(0);
|
||||
const hasFixedTree = noteContext?.hoistedNoteId === "_lbMobileRoot" && isMobile();
|
||||
const hasFixedTree = note && noteContext?.hoistedNoteId === "_lbMobileRoot" && isMobile() && note.noteId.startsWith("_lbMobile");
|
||||
|
||||
const props: TypeWidgetProps = {
|
||||
note: note!,
|
||||
@@ -216,7 +216,7 @@ export default function NoteDetail() {
|
||||
"fixed-tree": hasFixedTree
|
||||
})}
|
||||
>
|
||||
{hasFixedTree && <FixedTree />}
|
||||
{hasFixedTree && <FixedTree noteContext={noteContext} />}
|
||||
|
||||
{Object.entries(noteTypesToRender).map(([ itemType, Element ]) => {
|
||||
return <NoteDetailWrapper
|
||||
@@ -232,8 +232,7 @@ export default function NoteDetail() {
|
||||
);
|
||||
}
|
||||
|
||||
function FixedTree() {
|
||||
const { noteContext } = useNoteContext();
|
||||
function FixedTree({ noteContext }: { noteContext: NoteContext }) {
|
||||
const [ treeEl ] = useLegacyWidget(() => new NoteTreeWidget(), { noteContext });
|
||||
return <div class="fixed-note-tree-container">{treeEl}</div>;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ export default function GlobalMenu({ isHorizontalLayout }: { isHorizontalLayout:
|
||||
noDropdownListStyle
|
||||
mobileBackdrop
|
||||
>
|
||||
{isMobile() && <>
|
||||
<MenuItem command="searchNotes" icon="bx bx-search" text={t("global_menu.search_notes")} />
|
||||
<FormDropdownDivider />
|
||||
</>}
|
||||
|
||||
<MenuItem command="openNewWindow" icon="bx bx-window-open" text={t("global_menu.open_new_window")} />
|
||||
<MenuItem command="showShareSubtree" icon="bx bx-share-alt" text={t("global_menu.show_shared_notes_subtree")} />
|
||||
@@ -105,7 +109,7 @@ function BrowserOnlyOptions() {
|
||||
|
||||
function DevelopmentOptions({ dropStart }: { dropStart: boolean }) {
|
||||
return <>
|
||||
<FormListHeader text="Development Options"></FormListHeader>
|
||||
<FormListHeader text="Development Options" />
|
||||
<FormDropdownSubmenu icon="bx bx-test-tube" title="Experimental features" dropStart={dropStart}>
|
||||
{experimentalFeatures.map((feature) => (
|
||||
<ExperimentalFeatureToggle key={feature.id} experimentalFeature={feature as ExperimentalFeature} />
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
--card-padding: 0.6em;
|
||||
}
|
||||
|
||||
body.mobile .board-view {
|
||||
scroll-snap-type: x mandatory;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.board-view-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
@@ -26,6 +20,12 @@ body.mobile .board-view {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
body.mobile .board-view-container {
|
||||
scroll-snap-type: x mandatory;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.board-view-container .board-column {
|
||||
width: 250px;
|
||||
flex-shrink: 0;
|
||||
|
||||
@@ -18,14 +18,12 @@ import FlexContainer from "./flex_container.js";
|
||||
* - `#root-container.vertical-layout`, if the current layout is horizontal.
|
||||
*/
|
||||
export default class RootContainer extends FlexContainer<BasicWidget> {
|
||||
private originalViewportHeight: number;
|
||||
|
||||
constructor(isHorizontalLayout: boolean) {
|
||||
super(isHorizontalLayout ? "column" : "row");
|
||||
|
||||
this.id("root-widget");
|
||||
this.css("height", "100dvh");
|
||||
this.originalViewportHeight = getViewportHeight();
|
||||
}
|
||||
|
||||
render(): JQuery<HTMLElement> {
|
||||
@@ -40,6 +38,7 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
|
||||
this.#setThemeCapabilities();
|
||||
this.#setLocaleAndDirection(options.get("locale"));
|
||||
this.#setExperimentalFeatures();
|
||||
this.#initPWATopbarColor();
|
||||
|
||||
return super.render();
|
||||
}
|
||||
@@ -65,8 +64,12 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
|
||||
}
|
||||
|
||||
#onMobileResize() {
|
||||
const currentViewportHeight = getViewportHeight();
|
||||
const isKeyboardOpened = (currentViewportHeight < this.originalViewportHeight);
|
||||
const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
|
||||
const windowHeight = window.innerHeight;
|
||||
|
||||
// If viewport is significantly smaller, keyboard is likely open
|
||||
const isKeyboardOpened = windowHeight - viewportHeight > 150;
|
||||
|
||||
this.$widget.toggleClass("virtual-keyboard-opened", isKeyboardOpened);
|
||||
}
|
||||
|
||||
@@ -113,8 +116,23 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
|
||||
document.body.lang = locale;
|
||||
document.body.dir = correspondingLocale?.rtl ? "rtl" : "ltr";
|
||||
}
|
||||
|
||||
#initPWATopbarColor() {
|
||||
if (!utils.isPWA()) return;
|
||||
const tracker = $("#background-color-tracker");
|
||||
|
||||
if (tracker.length) {
|
||||
const applyThemeColor = () => {
|
||||
let meta = $("meta[name='theme-color']");
|
||||
if (!meta.length) {
|
||||
meta = $(`<meta name="theme-color">`).appendTo($("head"));
|
||||
}
|
||||
meta.attr("content", tracker.css("color"));
|
||||
};
|
||||
|
||||
tracker.on("transitionend", applyThemeColor);
|
||||
applyThemeColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getViewportHeight() {
|
||||
return window.visualViewport?.height ?? window.innerHeight;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { useContext, useMemo } from "preact/hooks";
|
||||
import { LaunchBarContext, LaunchBarDropdownButton, useLauncherIconAndTitle } from "./launch_bar_widgets";
|
||||
import { CSSProperties } from "preact";
|
||||
import type FNote from "../../entities/fnote";
|
||||
import { useChildNotes, useNoteLabelBoolean } from "../react/hooks";
|
||||
import "./BookmarkButtons.css";
|
||||
|
||||
import { CSSProperties } from "preact";
|
||||
import { useContext, useMemo } from "preact/hooks";
|
||||
|
||||
import type FNote from "../../entities/fnote";
|
||||
import { t } from "../../services/i18n";
|
||||
import { FormDropdownSubmenu, FormListItem } from "../react/FormList";
|
||||
import { useChildNotes, useNote, useNoteIcon, useNoteLabelBoolean } from "../react/hooks";
|
||||
import NoteLink from "../react/NoteLink";
|
||||
import { CustomNoteLauncher } from "./GenericButtons";
|
||||
import ResponsiveContainer from "../react/ResponsiveContainer";
|
||||
import { CustomNoteLauncher, launchCustomNoteLauncher } from "./GenericButtons";
|
||||
import { LaunchBarContext, LaunchBarDropdownButton, useLauncherIconAndTitle } from "./launch_bar_widgets";
|
||||
|
||||
const PARENT_NOTE_ID = "_lbBookmarks";
|
||||
|
||||
@@ -19,17 +24,64 @@ export default function BookmarkButtons() {
|
||||
const childNotes = useChildNotes(PARENT_NOTE_ID);
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
{childNotes?.map(childNote => <SingleBookmark key={childNote.noteId} note={childNote} />)}
|
||||
</div>
|
||||
)
|
||||
<ResponsiveContainer
|
||||
desktop={
|
||||
<div style={style}>
|
||||
{childNotes?.map(childNote => <SingleBookmark key={childNote.noteId} note={childNote} />)}
|
||||
</div>
|
||||
}
|
||||
mobile={
|
||||
<LaunchBarDropdownButton
|
||||
icon="bx bx-bookmark"
|
||||
title={t("bookmark_buttons.bookmarks")}
|
||||
>
|
||||
{childNotes?.map(childNote => <SingleBookmark key={childNote.noteId} note={childNote} />)}
|
||||
</LaunchBarDropdownButton>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SingleBookmark({ note }: { note: FNote }) {
|
||||
const [ bookmarkFolder ] = useNoteLabelBoolean(note, "bookmarkFolder");
|
||||
return bookmarkFolder
|
||||
? <BookmarkFolder note={note} />
|
||||
: <CustomNoteLauncher launcherNote={note} getTargetNoteId={() => note.noteId} />
|
||||
return <ResponsiveContainer
|
||||
desktop={
|
||||
bookmarkFolder
|
||||
? <BookmarkFolder note={note} />
|
||||
: <CustomNoteLauncher launcherNote={note} getTargetNoteId={() => note.noteId} />
|
||||
}
|
||||
mobile={<MobileBookmarkItem noteId={note.noteId} bookmarkFolder={bookmarkFolder} />}
|
||||
/>;
|
||||
}
|
||||
|
||||
function MobileBookmarkItem({ noteId, bookmarkFolder }: { noteId: string, bookmarkFolder: boolean }) {
|
||||
const note = useNote(noteId);
|
||||
const noteIcon = useNoteIcon(note);
|
||||
if (!note) return null;
|
||||
|
||||
return (
|
||||
!bookmarkFolder
|
||||
? <FormListItem icon={noteIcon} onClick={(e) => launchCustomNoteLauncher(e, { launcherNote: note, getTargetNoteId: () => note.noteId })}>{note.title}</FormListItem>
|
||||
: <MobileBookmarkFolder note={note} />
|
||||
);
|
||||
}
|
||||
|
||||
function MobileBookmarkFolder({ note }: { note: FNote }) {
|
||||
const childNotes = useChildNotes(note.noteId);
|
||||
|
||||
return (
|
||||
<FormDropdownSubmenu icon="bx bx-folder" title={note.title}>
|
||||
{childNotes.map(childNote => (
|
||||
<FormListItem
|
||||
key={childNote.noteId}
|
||||
icon={childNote.getIcon()}
|
||||
onClick={(e) => launchCustomNoteLauncher(e, { launcherNote: childNote, getTargetNoteId: () => childNote.noteId })}
|
||||
>
|
||||
{childNote.title}
|
||||
</FormListItem>
|
||||
))}
|
||||
</FormDropdownSubmenu>
|
||||
);
|
||||
}
|
||||
|
||||
function BookmarkFolder({ note }: { note: FNote }) {
|
||||
@@ -55,5 +107,5 @@ function BookmarkFolder({ note }: { note: FNote }) {
|
||||
</ul>
|
||||
</div>
|
||||
</LaunchBarDropdownButton>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,32 +7,18 @@ import { isCtrlKey } from "../../services/utils";
|
||||
import { useGlobalShortcut, useNoteLabel } from "../react/hooks";
|
||||
import { LaunchBarActionButton, useLauncherIconAndTitle } from "./launch_bar_widgets";
|
||||
|
||||
export function CustomNoteLauncher({ launcherNote, getTargetNoteId, getHoistedNoteId }: {
|
||||
export function CustomNoteLauncher(props: {
|
||||
launcherNote: FNote;
|
||||
getTargetNoteId: (launcherNote: FNote) => string | null | Promise<string | null>;
|
||||
getHoistedNoteId?: (launcherNote: FNote) => string | null;
|
||||
keyboardShortcut?: string;
|
||||
}) {
|
||||
const { launcherNote, getTargetNoteId } = props;
|
||||
const { icon, title } = useLauncherIconAndTitle(launcherNote);
|
||||
|
||||
const launch = useCallback(async (evt: MouseEvent | KeyboardEvent) => {
|
||||
if (evt.which === 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetNoteId = await getTargetNoteId(launcherNote);
|
||||
if (!targetNoteId) return;
|
||||
|
||||
const hoistedNoteIdWithDefault = getHoistedNoteId?.(launcherNote) || appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||
const ctrlKey = isCtrlKey(evt);
|
||||
|
||||
if ((evt.which === 1 && ctrlKey) || evt.which === 2) {
|
||||
const activate = !!evt.shiftKey;
|
||||
await appContext.tabManager.openInNewTab(targetNoteId, hoistedNoteIdWithDefault, activate);
|
||||
} else {
|
||||
await appContext.tabManager.openInSameTab(targetNoteId, hoistedNoteIdWithDefault);
|
||||
}
|
||||
}, [ launcherNote, getTargetNoteId, getHoistedNoteId ]);
|
||||
await launchCustomNoteLauncher(evt, props);
|
||||
}, [ props ]);
|
||||
|
||||
// Keyboard shortcut.
|
||||
const [ shortcut ] = useNoteLabel(launcherNote, "keyboardShortcut");
|
||||
@@ -54,3 +40,24 @@ export function CustomNoteLauncher({ launcherNote, getTargetNoteId, getHoistedNo
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export async function launchCustomNoteLauncher(evt: MouseEvent | KeyboardEvent, { launcherNote, getTargetNoteId, getHoistedNoteId }: {
|
||||
launcherNote: FNote;
|
||||
getTargetNoteId: (launcherNote: FNote) => string | null | Promise<string | null>;
|
||||
getHoistedNoteId?: (launcherNote: FNote) => string | null;
|
||||
}) {
|
||||
if (evt.which === 3) return;
|
||||
|
||||
const targetNoteId = await getTargetNoteId(launcherNote);
|
||||
if (!targetNoteId) return;
|
||||
|
||||
const hoistedNoteIdWithDefault = getHoistedNoteId?.(launcherNote) || appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||
const ctrlKey = isCtrlKey(evt);
|
||||
|
||||
if ((evt.which === 1 && ctrlKey) || evt.which === 2) {
|
||||
const activate = !!evt.shiftKey;
|
||||
await appContext.tabManager.openInNewTab(targetNoteId, hoistedNoteIdWithDefault, activate);
|
||||
} else {
|
||||
await appContext.tabManager.openInSameTab(targetNoteId, hoistedNoteIdWithDefault);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ export function LaunchBarDropdownButton({ children, icon, dropdownOptions, ...pr
|
||||
placement: isHorizontalLayout ? "bottom" : "right"
|
||||
}
|
||||
}}
|
||||
mobileBackdrop
|
||||
{...props}
|
||||
>{children}</Dropdown>
|
||||
);
|
||||
|
||||
@@ -50,6 +50,9 @@ body.experimental-feature-new-layout {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
body.desktop .title-actions {
|
||||
> .collapsible,
|
||||
> .note-type-switcher {
|
||||
padding-inline-start: calc(24px - var(--title-actions-padding-start));
|
||||
@@ -57,3 +60,4 @@ body.experimental-feature-new-layout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function NoteTitleActions() {
|
||||
<PromotedAttributes note={note} componentId={componentId} noteContext={noteContext} />
|
||||
{noteType === "search" && <SearchProperties note={note} ntxId={ntxId} />}
|
||||
<EditedNotes />
|
||||
{viewScope?.viewMode === "default" && <NoteTypeSwitcher />}
|
||||
{(!viewScope?.viewMode || viewScope.viewMode === "default") && <NoteTypeSwitcher />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,33 +58,6 @@
|
||||
|
||||
.dropdown-note-info {
|
||||
padding: 1em !important;
|
||||
|
||||
ul {
|
||||
--row-block-margin: .2em;
|
||||
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-top: calc(0px - var(--row-block-margin));
|
||||
margin-bottom: 12px;
|
||||
display: table;
|
||||
|
||||
li {
|
||||
display: table-row;
|
||||
|
||||
> strong {
|
||||
display: table-cell;
|
||||
padding: var(--row-block-margin) 0;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
> span {
|
||||
display: table-cell;
|
||||
user-select: text;
|
||||
padding-left: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-note-paths {
|
||||
@@ -144,16 +117,50 @@
|
||||
|
||||
}
|
||||
|
||||
div.similar-notes-widget div.similar-notes-wrapper {
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
button.select-button:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
body.experimental-feature-new-layout .note-info-content {
|
||||
ul {
|
||||
--row-block-margin: .2em;
|
||||
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-top: calc(0px - var(--row-block-margin));
|
||||
margin-bottom: 12px;
|
||||
display: table;
|
||||
|
||||
li {
|
||||
display: table-row;
|
||||
|
||||
> strong {
|
||||
display: table-cell;
|
||||
padding: var(--row-block-margin) 0;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
> span {
|
||||
display: table-cell;
|
||||
user-select: text;
|
||||
padding-left: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.experimental-feature-new-layout div.similar-notes-widget div.similar-notes-wrapper {
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
body.experimental-feature-new-layout.mobile div.similar-notes-widget div.similar-notes-wrapper {
|
||||
max-height: unset;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.bottom-panel {
|
||||
margin: 0 !important;
|
||||
padding: 0;
|
||||
|
||||
@@ -212,8 +212,8 @@ export function getLocaleName(locale: Locale | null | undefined) {
|
||||
|
||||
//#region Note info & Similar
|
||||
interface NoteInfoContext extends StatusBarContext {
|
||||
similarNotesShown: boolean;
|
||||
setSimilarNotesShown: (value: boolean) => void;
|
||||
similarNotesShown?: boolean;
|
||||
setSimilarNotesShown?: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export function NoteInfoBadge(context: NoteInfoContext) {
|
||||
@@ -225,7 +225,7 @@ export function NoteInfoBadge(context: NoteInfoContext) {
|
||||
|
||||
// Keyboard shortcut.
|
||||
useTriliumEvent("toggleRibbonTabNoteInfo", () => enabled && dropdownRef.current?.show());
|
||||
useTriliumEvent("toggleRibbonTabSimilarNotes", () => setSimilarNotesShown(!similarNotesShown));
|
||||
useTriliumEvent("toggleRibbonTabSimilarNotes", () => setSimilarNotesShown && setSimilarNotesShown(!similarNotesShown));
|
||||
|
||||
return (enabled &&
|
||||
<StatusBarDropdown
|
||||
@@ -242,8 +242,8 @@ export function NoteInfoBadge(context: NoteInfoContext) {
|
||||
);
|
||||
}
|
||||
|
||||
function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdownRef }: NoteInfoContext & {
|
||||
dropdownRef: RefObject<BootstrapDropdown>;
|
||||
export function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdownRef }: Pick<NoteInfoContext, "note" | "setSimilarNotesShown"> & {
|
||||
dropdownRef?: RefObject<BootstrapDropdown>;
|
||||
noteType: NoteType;
|
||||
}) {
|
||||
const { metadata, ...sizeProps } = useNoteMetadata(note);
|
||||
@@ -251,7 +251,7 @@ function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdownRef }:
|
||||
const noteTypeMapping = useMemo(() => NOTE_TYPES.find(t => t.type === noteType), [ noteType ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="note-info-content">
|
||||
<ul>
|
||||
{originalFileName && <NoteInfoValue text={t("file_properties.original_file_name")} value={originalFileName} />}
|
||||
<NoteInfoValue text={t("note_info_widget.created")} value={formatDateTime(metadata?.dateCreated)} />
|
||||
@@ -262,14 +262,14 @@ function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdownRef }:
|
||||
<NoteInfoValue text={t("note_info_widget.note_size")} title={t("note_info_widget.note_size_info")} value={<NoteSizeWidget {...sizeProps} />} />
|
||||
</ul>
|
||||
|
||||
<LinkButton
|
||||
{setSimilarNotesShown && <LinkButton
|
||||
text={t("note_info_widget.show_similar_notes")}
|
||||
onClick={() => {
|
||||
dropdownRef.current?.hide();
|
||||
dropdownRef?.current?.hide();
|
||||
setSimilarNotesShown(true);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.code-note-switcher-modal .dropdown-menu {
|
||||
background: none !important;
|
||||
}
|
||||
@@ -1,17 +1,24 @@
|
||||
import "./mobile_detail_menu.css";
|
||||
|
||||
import { Dropdown as BootstrapDropdown } from "bootstrap";
|
||||
import { createPortal, useRef, useState } from "preact/compat";
|
||||
|
||||
import FNote, { NotePathRecord } from "../../entities/fnote";
|
||||
import { t } from "../../services/i18n";
|
||||
import note_create from "../../services/note_create";
|
||||
import server from "../../services/server";
|
||||
import { BacklinksList, useBacklinkCount } from "../FloatingButtonsDefinitions";
|
||||
import { getLocaleName, NoteInfoContent } from "../layout/StatusBar";
|
||||
import ActionButton from "../react/ActionButton";
|
||||
import { FormDropdownDivider, FormListItem } from "../react/FormList";
|
||||
import { useNoteContext } from "../react/hooks";
|
||||
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../react/FormList";
|
||||
import { useNoteContext, useNoteProperty } from "../react/hooks";
|
||||
import Modal from "../react/Modal";
|
||||
import { NoteTypeCodeNoteList, useLanguageSwitcher, useMimeTypes } from "../ribbon/BasicPropertiesTab";
|
||||
import { NoteContextMenu } from "../ribbon/NoteActions";
|
||||
import NoteActionsCustom from "../ribbon/NoteActionsCustom";
|
||||
import { NotePathsWidget, useSortedNotePaths } from "../ribbon/NotePathsTab";
|
||||
import SimilarNotesTab from "../ribbon/SimilarNotesTab";
|
||||
import { useProcessedLocales } from "../type_widgets/options/components/LocaleSelector";
|
||||
|
||||
export default function MobileDetailMenu() {
|
||||
const dropdownRef = useRef<BootstrapDropdown | null>(null);
|
||||
@@ -20,6 +27,9 @@ export default function MobileDetailMenu() {
|
||||
const isMainContext = noteContext?.isMainContext();
|
||||
const [ backlinksModalShown, setBacklinksModalShown ] = useState(false);
|
||||
const [ notePathsModalShown, setNotePathsModalShown ] = useState(false);
|
||||
const [ noteInfoModalShown, setNoteInfoModalShown ] = useState(false);
|
||||
const [ similarNotesModalShown, setSimilarNotesModalShown ] = useState(false);
|
||||
const [ codeNoteSwitcherModalShown, setCodeNoteSwitcherModalShown ] = useState(false);
|
||||
const sortedNotePaths = useSortedNotePaths(note, hoistedNoteId);
|
||||
const backlinksCount = useBacklinkCount(note, viewScope?.viewMode === "default");
|
||||
|
||||
@@ -36,7 +46,7 @@ export default function MobileDetailMenu() {
|
||||
<NoteContextMenu
|
||||
dropdownRef={dropdownRef}
|
||||
note={note} noteContext={noteContext}
|
||||
extraItems={<>
|
||||
itemsAtStart={<>
|
||||
<div className="form-list-row">
|
||||
<div className="form-list-col">
|
||||
<FormListItem
|
||||
@@ -81,6 +91,13 @@ export default function MobileDetailMenu() {
|
||||
</>}
|
||||
<FormDropdownDivider />
|
||||
</>}
|
||||
itemsNearNoteSettings={<>
|
||||
{note.type === "text" && <ContentLanguageSelector note={note} />}
|
||||
{note.type === "code" && <FormListItem icon={"bx bx-code"} onClick={() => setCodeNoteSwitcherModalShown(true)}>{t("status_bar.code_note_switcher")}</FormListItem>}
|
||||
<FormListItem icon="bx bx-info-circle" onClick={() => setNoteInfoModalShown(true)}>{t("note_info_widget.title")}</FormListItem>
|
||||
<FormListItem icon="bx bx-bar-chart" onClick={() => setSimilarNotesModalShown(true)}>{t("similar_notes.title")}</FormListItem>
|
||||
<FormDropdownDivider />
|
||||
</>}
|
||||
/>
|
||||
) : (
|
||||
<ActionButton
|
||||
@@ -94,13 +111,46 @@ export default function MobileDetailMenu() {
|
||||
<>
|
||||
<BacklinksModal note={note} modalShown={backlinksModalShown} setModalShown={setBacklinksModalShown} />
|
||||
<NotePathsModal note={note} modalShown={notePathsModalShown} notePath={noteContext?.notePath} sortedNotePaths={sortedNotePaths} setModalShown={setNotePathsModalShown} />
|
||||
<NoteInfoModal note={note} modalShown={noteInfoModalShown} setModalShown={setNoteInfoModalShown} />
|
||||
<SimilarNotesModal note={note} modalShown={similarNotesModalShown} setModalShown={setSimilarNotesModalShown} />
|
||||
<CodeNoteSwitcherModal note={note} modalShown={codeNoteSwitcherModalShown} setModalShown={setCodeNoteSwitcherModalShown} />
|
||||
</>
|
||||
), document.body)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BacklinksModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined, modalShown: boolean, setModalShown: (shown: boolean) => void }) {
|
||||
function ContentLanguageSelector({ note }: { note: FNote | null | undefined }) {
|
||||
const { locales, DEFAULT_LOCALE, currentNoteLanguage, setCurrentNoteLanguage } = useLanguageSwitcher(note);
|
||||
const { activeLocale, processedLocales } = useProcessedLocales(locales, DEFAULT_LOCALE, currentNoteLanguage ?? DEFAULT_LOCALE.id);
|
||||
|
||||
return (
|
||||
<FormDropdownSubmenu
|
||||
icon="bx bx-globe"
|
||||
title={t("mobile_detail_menu.content_language_switcher", { language: getLocaleName(activeLocale ?? DEFAULT_LOCALE) })}
|
||||
>
|
||||
{processedLocales.map((locale, index) =>
|
||||
(typeof locale === "object") ? (
|
||||
<FormListItem
|
||||
key={locale.id}
|
||||
rtl={locale.rtl}
|
||||
checked={locale.id === currentNoteLanguage}
|
||||
onClick={() => setCurrentNoteLanguage(locale.id)}
|
||||
>{locale.name}</FormListItem>
|
||||
) : (
|
||||
<FormDropdownDivider key={`divider-${index}`} />
|
||||
)
|
||||
)}
|
||||
</FormDropdownSubmenu>
|
||||
);
|
||||
}
|
||||
|
||||
interface WithModal {
|
||||
modalShown: boolean;
|
||||
setModalShown: (shown: boolean) => void;
|
||||
}
|
||||
|
||||
function BacklinksModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined } & WithModal) {
|
||||
return (
|
||||
<Modal
|
||||
className="backlinks-modal tn-backlinks-widget"
|
||||
@@ -116,7 +166,7 @@ function BacklinksModal({ note, modalShown, setModalShown }: { note: FNote | nul
|
||||
);
|
||||
}
|
||||
|
||||
function NotePathsModal({ note, modalShown, notePath, sortedNotePaths, setModalShown }: { note: FNote | null | undefined, modalShown: boolean, sortedNotePaths: NotePathRecord[] | undefined, notePath: string | null | undefined, setModalShown: (shown: boolean) => void }) {
|
||||
function NotePathsModal({ note, modalShown, notePath, sortedNotePaths, setModalShown }: { note: FNote | null | undefined, sortedNotePaths: NotePathRecord[] | undefined, notePath: string | null | undefined } & WithModal) {
|
||||
return (
|
||||
<Modal
|
||||
className="note-paths-modal"
|
||||
@@ -134,3 +184,57 @@ function NotePathsModal({ note, modalShown, notePath, sortedNotePaths, setModalS
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function NoteInfoModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined } & WithModal) {
|
||||
return (
|
||||
<Modal
|
||||
className="note-info-modal"
|
||||
size="md"
|
||||
title={t("note_info_widget.title")}
|
||||
show={modalShown}
|
||||
onHidden={() => setModalShown(false)}
|
||||
>
|
||||
{note && <NoteInfoContent note={note} noteType={note.type} />}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function SimilarNotesModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined } & WithModal) {
|
||||
return (
|
||||
<Modal
|
||||
className="similar-notes-modal"
|
||||
size="md"
|
||||
title={t("similar_notes.title")}
|
||||
show={modalShown}
|
||||
onHidden={() => setModalShown(false)}
|
||||
>
|
||||
<SimilarNotesTab note={note} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function CodeNoteSwitcherModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined } & WithModal) {
|
||||
const currentNoteMime = useNoteProperty(note, "mime");
|
||||
const mimeTypes = useMimeTypes();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="code-note-switcher-modal"
|
||||
size="md"
|
||||
title={t("status_bar.code_note_switcher")}
|
||||
show={modalShown}
|
||||
onHidden={() => setModalShown(false)}
|
||||
>
|
||||
<div className="dropdown-menu static show">
|
||||
{note && <NoteTypeCodeNoteList
|
||||
currentMimeType={currentNoteMime}
|
||||
mimeTypes={mimeTypes}
|
||||
changeNoteType={(type, mime) => {
|
||||
server.put(`notes/${note.noteId}/type`, { type, mime });
|
||||
setModalShown(false);
|
||||
}}
|
||||
/>}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
12
apps/client/src/widgets/react/ResponsiveContainer.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ComponentChildren } from "preact";
|
||||
|
||||
import { isMobile } from "../../services/utils";
|
||||
|
||||
interface ResponsiveContainerProps {
|
||||
mobile?: ComponentChildren;
|
||||
desktop?: ComponentChildren;
|
||||
}
|
||||
|
||||
export default function ResponsiveContainer({ mobile, desktop }: ResponsiveContainerProps) {
|
||||
return (isMobile() ? mobile : desktop);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { MimeType, NoteType, ToggleInParentResponse } from "@triliumnext/commons";
|
||||
import { ComponentChildren } from "preact";
|
||||
import { createPortal } from "preact/compat";
|
||||
import { Dispatch, StateUpdater, useCallback, useEffect, useMemo, useState } from "preact/hooks";
|
||||
|
||||
@@ -117,19 +116,18 @@ export function NoteTypeDropdownContent({ currentNoteType, currentNoteMime, note
|
||||
onClick={() => changeNoteType(type, mime)}
|
||||
>{title}</FormListItem>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<FormDropdownDivider />
|
||||
<FormListItem
|
||||
checked={checked}
|
||||
disabled
|
||||
>
|
||||
<strong>{title}</strong>
|
||||
</FormListItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<FormDropdownDivider />
|
||||
<FormListItem
|
||||
checked={checked}
|
||||
disabled
|
||||
>
|
||||
<strong>{title}</strong>
|
||||
</FormListItem>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
|
||||
{!noCodeNotes && <NoteTypeCodeNoteList mimeTypes={mimeTypes} changeNoteType={changeNoteType} setModalShown={setModalShown} />}
|
||||
@@ -141,7 +139,7 @@ export function NoteTypeCodeNoteList({ currentMimeType, mimeTypes, changeNoteTyp
|
||||
currentMimeType?: string;
|
||||
mimeTypes: MimeType[];
|
||||
changeNoteType(type: NoteType, mime: string): void;
|
||||
setModalShown(shown: boolean): void;
|
||||
setModalShown?(shown: boolean): void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
@@ -155,8 +153,10 @@ export function NoteTypeCodeNoteList({ currentMimeType, mimeTypes, changeNoteTyp
|
||||
</FormListItem>
|
||||
))}
|
||||
|
||||
<FormDropdownDivider />
|
||||
<FormListItem icon="bx bx-cog" onClick={() => setModalShown(true)}>{t("basic_properties.configure_code_notes")}</FormListItem>
|
||||
{setModalShown && <>
|
||||
<FormDropdownDivider />
|
||||
<FormListItem icon="bx bx-cog" onClick={() => setModalShown(true)}>{t("basic_properties.configure_code_notes")}</FormListItem>
|
||||
</>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -195,7 +195,7 @@ function ProtectedNoteSwitch({ note }: { note?: FNote | null }) {
|
||||
onChange={(shouldProtect) => note && protected_session.protectNote(note.noteId, shouldProtect, false)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function EditabilitySelect({ note }: { note?: FNote | null }) {
|
||||
@@ -417,9 +417,9 @@ function findTypeTitle(type?: NoteType, mime?: string | null) {
|
||||
const found = mimeTypes.find((mt) => mt.mime === mime);
|
||||
|
||||
return found ? found.title : mime;
|
||||
} else {
|
||||
const noteType = NOTE_TYPES.find((nt) => nt.type === type);
|
||||
|
||||
return noteType ? noteType.title : type;
|
||||
}
|
||||
const noteType = NOTE_TYPES.find((nt) => nt.type === type);
|
||||
|
||||
return noteType ? noteType.title : type;
|
||||
|
||||
}
|
||||
|
||||
@@ -63,10 +63,11 @@ function RevisionsButton({ note }: { note: FNote }) {
|
||||
|
||||
type ItemToFocus = "basic-properties";
|
||||
|
||||
export function NoteContextMenu({ note, noteContext, extraItems, dropdownRef: externalDropdownRef }: {
|
||||
export function NoteContextMenu({ note, noteContext, itemsAtStart, itemsNearNoteSettings, dropdownRef: externalDropdownRef }: {
|
||||
note: FNote,
|
||||
noteContext?: NoteContext,
|
||||
extraItems?: ComponentChildren;
|
||||
itemsAtStart?: ComponentChildren;
|
||||
itemsNearNoteSettings?: ComponentChildren;
|
||||
dropdownRef?: RefObject<BootstrapDropdown>;
|
||||
}) {
|
||||
const dropdownRef = useSyncedRef<BootstrapDropdown>(externalDropdownRef, null);
|
||||
@@ -112,7 +113,7 @@ export function NoteContextMenu({ note, noteContext, extraItems, dropdownRef: ex
|
||||
onHidden={() => itemToFocusRef.current = null }
|
||||
mobileBackdrop
|
||||
>
|
||||
{extraItems}
|
||||
{itemsAtStart}
|
||||
|
||||
{isReadOnly && <>
|
||||
<CommandItem icon="bx bx-pencil" text={t("read-only-info.edit-note")}
|
||||
@@ -131,6 +132,8 @@ export function NoteContextMenu({ note, noteContext, extraItems, dropdownRef: ex
|
||||
<FormDropdownDivider />
|
||||
</>}
|
||||
|
||||
{itemsNearNoteSettings}
|
||||
|
||||
<CommandItem icon="bx bx-import" text={t("note_actions.import_files")}
|
||||
disabled={isInOptionsOrHelp || note.type === "search"}
|
||||
command={() => parentComponent?.triggerCommand("showImportDialog", { noteId: note.noteId })} />
|
||||
|
||||
@@ -2,6 +2,7 @@ import "./SearchDefinitionTab.css";
|
||||
|
||||
import { SaveSearchNoteResponse } from "@triliumnext/commons";
|
||||
import { useContext, useEffect, useState } from "preact/hooks";
|
||||
import { Fragment } from "preact/jsx-runtime";
|
||||
|
||||
import appContext from "../../components/app_context";
|
||||
import FNote from "../../entities/fnote";
|
||||
@@ -15,12 +16,13 @@ import tree from "../../services/tree";
|
||||
import { getErrorMessage } from "../../services/utils";
|
||||
import ws from "../../services/ws";
|
||||
import RenameNoteBulkAction from "../bulk_actions/note/rename_note";
|
||||
import Button from "../react/Button";
|
||||
import Button, { SplitButton } from "../react/Button";
|
||||
import Dropdown from "../react/Dropdown";
|
||||
import { FormListHeader, FormListItem } from "../react/FormList";
|
||||
import { useTriliumEvent } from "../react/hooks";
|
||||
import Icon from "../react/Icon";
|
||||
import { ParentComponent } from "../react/react_utils";
|
||||
import ResponsiveContainer from "../react/ResponsiveContainer";
|
||||
import { TabContext } from "./ribbon-interface";
|
||||
import { SEARCH_OPTIONS, SearchOption } from "./SearchDefinitionOptions";
|
||||
|
||||
@@ -84,15 +86,32 @@ export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabCon
|
||||
<tr>
|
||||
<td className="title-column">{t("search_definition.add_search_option")}</td>
|
||||
<td colSpan={2} className="add-search-option">
|
||||
{searchOptions?.availableOptions.map(({ icon, label, tooltip, attributeName, attributeType, defaultValue }) => (
|
||||
<Button
|
||||
size="small"
|
||||
icon={icon}
|
||||
text={label}
|
||||
title={tooltip}
|
||||
onClick={() => attributes.setAttribute(note, attributeType, attributeName, defaultValue ?? "")}
|
||||
/>
|
||||
))}
|
||||
<ResponsiveContainer
|
||||
desktop={searchOptions?.availableOptions.map(({ icon, label, tooltip, attributeName, attributeType, defaultValue }) => (
|
||||
<Button
|
||||
key={`${attributeType}-${attributeName}`}
|
||||
size="small" icon={icon} text={label} title={tooltip}
|
||||
onClick={() => attributes.setAttribute(note, attributeType, attributeName, defaultValue ?? "")}
|
||||
/>
|
||||
))}
|
||||
mobile={
|
||||
<Dropdown
|
||||
buttonClassName="action-add-toggle btn btn-sm"
|
||||
text={<><Icon icon="bx bx-plus" />{" "}{t("search_definition.option")}</>}
|
||||
dropdownContainerClassName="mobile-bottom-menu" mobileBackdrop
|
||||
noSelectButtonStyle
|
||||
>
|
||||
{searchOptions?.availableOptions.map(({ icon, label, tooltip, attributeName, attributeType, defaultValue }) => (
|
||||
<FormListItem
|
||||
key={`${attributeType}-${attributeName}`}
|
||||
icon={icon}
|
||||
description={tooltip}
|
||||
onClick={() => attributes.setAttribute(note, attributeType, attributeName, defaultValue ?? "")}
|
||||
>{label}</FormListItem>
|
||||
))}
|
||||
</Dropdown>
|
||||
}
|
||||
/>
|
||||
|
||||
<AddBulkActionButton note={note} />
|
||||
</td>
|
||||
@@ -113,48 +132,7 @@ export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabCon
|
||||
})}
|
||||
</tbody>
|
||||
<BulkActionsList note={note} />
|
||||
<tbody className="search-actions">
|
||||
<tr>
|
||||
<td colSpan={3}>
|
||||
<div className="search-actions-container">
|
||||
<Button
|
||||
icon="bx bx-search"
|
||||
text={t("search_definition.search_button")}
|
||||
keyboardShortcut="Enter"
|
||||
onClick={refreshResults}
|
||||
/>
|
||||
|
||||
<Button
|
||||
icon="bx bxs-zap"
|
||||
text={t("search_definition.search_execute")}
|
||||
onClick={async () => {
|
||||
await server.post(`search-and-execute-note/${note.noteId}`);
|
||||
refreshResults();
|
||||
toast.showMessage(t("search_definition.actions_executed"), 3000);
|
||||
}}
|
||||
/>
|
||||
|
||||
{note.isHiddenCompletely() && <Button
|
||||
icon="bx bx-save"
|
||||
text={t("search_definition.save_to_note")}
|
||||
onClick={async () => {
|
||||
const { notePath } = await server.post<SaveSearchNoteResponse>("special-notes/save-search-note", { searchNoteId: note.noteId });
|
||||
if (!notePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
await ws.waitForMaxKnownEntityChangeId();
|
||||
await appContext.tabManager.getActiveContext()?.setNote(notePath);
|
||||
|
||||
// Note the {{- notePathTitle}} in json file is not typo, it's unescaping
|
||||
// See https://www.i18next.com/translation-function/interpolation#unescape
|
||||
toast.showMessage(t("search_definition.search_note_saved", { notePathTitle: await tree.getNotePathTitle(notePath) }));
|
||||
}}
|
||||
/>}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<SearchButtonBar note={note} refreshResults={refreshResults} />
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
@@ -162,6 +140,56 @@ export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabCon
|
||||
);
|
||||
}
|
||||
|
||||
function SearchButtonBar({ note, refreshResults }: {
|
||||
note: FNote;
|
||||
refreshResults(): void;
|
||||
}) {
|
||||
async function searchAndExecuteActions() {
|
||||
await server.post(`search-and-execute-note/${note.noteId}`);
|
||||
refreshResults();
|
||||
toast.showMessage(t("search_definition.actions_executed"), 3000);
|
||||
}
|
||||
|
||||
async function saveSearchNote() {
|
||||
const { notePath } = await server.post<SaveSearchNoteResponse>("special-notes/save-search-note", { searchNoteId: note.noteId });
|
||||
if (!notePath) return;
|
||||
|
||||
await ws.waitForMaxKnownEntityChangeId();
|
||||
await appContext.tabManager.getActiveContext()?.setNote(notePath);
|
||||
|
||||
// Note the {{- notePathTitle}} in json file is not typo, it's unescaping
|
||||
// See https://www.i18next.com/translation-function/interpolation#unescape
|
||||
toast.showMessage(t("search_definition.search_note_saved", { notePathTitle: await tree.getNotePathTitle(notePath) }));
|
||||
}
|
||||
|
||||
return (
|
||||
<tbody className="search-actions">
|
||||
<tr>
|
||||
<td colSpan={3}>
|
||||
<ResponsiveContainer
|
||||
desktop={
|
||||
<div className="search-actions-container">
|
||||
<Button icon="bx bx-search" text={t("search_definition.search_button")} keyboardShortcut="Enter" onClick={refreshResults} />
|
||||
<Button icon="bx bxs-zap" text={t("search_definition.search_execute")} onClick={searchAndExecuteActions} />
|
||||
{note.isHiddenCompletely() && <Button icon="bx bx-save" text={t("search_definition.save_to_note")} onClick={saveSearchNote} />}
|
||||
</div>
|
||||
}
|
||||
mobile={
|
||||
<SplitButton
|
||||
text={t("search_definition.search_button")} icon="bx bx-search"
|
||||
onClick={refreshResults}
|
||||
>
|
||||
<FormListItem icon="bx bxs-zap" onClick={searchAndExecuteActions}>{t("search_definition.search_execute")}</FormListItem>
|
||||
{note.isHiddenCompletely() && <FormListItem icon="bx bx-save" onClick={saveSearchNote}>{t("search_definition.save_to_note")}</FormListItem>}
|
||||
</SplitButton>
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
);
|
||||
}
|
||||
|
||||
function BulkActionsList({ note }: { note: FNote }) {
|
||||
const [ bulkActions, setBulkActions ] = useState<RenameNoteBulkAction[]>();
|
||||
|
||||
@@ -194,15 +222,18 @@ function AddBulkActionButton({ note }: { note: FNote }) {
|
||||
buttonClassName="action-add-toggle btn btn-sm"
|
||||
text={<><Icon icon="bx bxs-zap" />{" "}{t("search_definition.action")}</>}
|
||||
noSelectButtonStyle
|
||||
dropdownContainerClassName="mobile-bottom-menu" mobileBackdrop
|
||||
>
|
||||
{ACTION_GROUPS.map(({ actions, title }) => (
|
||||
<>
|
||||
{ACTION_GROUPS.map(({ actions, title }, index) => (
|
||||
<Fragment key={index}>
|
||||
<FormListHeader text={title} />
|
||||
|
||||
{actions.map(({ actionName, actionTitle }) => (
|
||||
<FormListItem onClick={() => bulk_action.addAction(note.noteId, actionName)}>{actionTitle}</FormListItem>
|
||||
))}
|
||||
</>
|
||||
<div>
|
||||
{actions.map(({ actionName, actionTitle }) => (
|
||||
<FormListItem key={actionName} onClick={() => bulk_action.addAction(note.noteId, actionName)}>{actionTitle}</FormListItem>
|
||||
))}
|
||||
</div>
|
||||
</Fragment>
|
||||
))}
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
|
||||
@media (max-width: 991px) {
|
||||
margin-block: 1em;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
}
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
@@ -42,6 +48,12 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1em;
|
||||
|
||||
@media (max-width: 991px) {
|
||||
gap: 0.5em;
|
||||
flex-wrap: wrap;
|
||||
.attachment-title { flex-grow: 1; }
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-details {
|
||||
@@ -127,10 +139,6 @@
|
||||
|
||||
/* #region Attachment actions */
|
||||
|
||||
.attachment-actions .dropdown-menu {
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
.attachment-actions .dropdown-item .tn-icon {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
|
||||
@@ -239,6 +239,8 @@ function AttachmentActions({ attachment, copyAttachmentLinkToClipboard }: { atta
|
||||
text={<Icon icon="bx bx-dots-vertical-rounded" />}
|
||||
buttonClassName="icon-action-always-border"
|
||||
iconAction
|
||||
dropdownContainerClassName="mobile-bottom-menu"
|
||||
mobileBackdrop
|
||||
>
|
||||
<FormListItem
|
||||
icon="bx bx-file-find"
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
<p>To easily access selected notes, you can bookmark them. See demo:</p>
|
||||
<p>
|
||||
<img src="Bookmarks_bookmarks.gif">
|
||||
</p>
|
||||
<p>Frequently used notes can be bookmarked, which will make them appear in
|
||||
the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a> for
|
||||
easy access.</p>
|
||||
<h2>Configuring the launch bar</h2>
|
||||
<p>If bookmarks don't appear in the launch bar, then most likely the bookmark
|
||||
section has been hidden. Go to the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a> configuration
|
||||
from the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
ensure <em>Bookmarks</em> is in the <em>Visible Launchers</em> section.</p>
|
||||
<h2>Bookmark folder</h2>
|
||||
<p>Space in the left panel is limited, and you might want to bookmark many
|
||||
items. One possible solution is to bookmark a folder, so it shows its children:</p>
|
||||
<p>
|
||||
<img src="Bookmarks_bookmark-folder.png">
|
||||
</p>
|
||||
<p>To do this, you need to add a <code spellcheck="false">#bookmarkFolder</code> label
|
||||
to the note.</p>
|
||||
<p>To do this, bookmark a folder and assign it the <code spellcheck="false">#bookmarkFolder</code> label.</p>
|
||||
<h2>Mobile</h2>
|
||||
<p>On mobile, bookmarks are only displayed starting with v0.102.0. Because
|
||||
of the more constrained screen size, the bookmarks are grouped under a
|
||||
single icon instead of displaying them as separate icons.</p>
|
||||
<p>When pressed, a menu will appear listing all the bookmarks. Bookmark folders
|
||||
are also supported and will appear as sub-menus.</p>
|
||||
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 305 KiB |
@@ -1,8 +1,11 @@
|
||||
<h2>Position of the Launch bar</h2>
|
||||
<p>Depending on the layout selected, the launcher bar will either be on the
|
||||
left side of the screen with buttons displayed vertically or at the top
|
||||
of the screen. See <a href="#root/_help_x0JgW8UqGXvq">Vertical and horizontal layout</a> for
|
||||
<p>On desktop, depending on the layout selected, the launcher bar will either
|
||||
be on the left side of the screen with buttons displayed vertically or
|
||||
at the top of the screen. See <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x0JgW8UqGXvq">Vertical and horizontal layout</a> for
|
||||
more information.</p>
|
||||
<p>On mobile, the launch bar will always be at the bottom.</p>
|
||||
<p>If there are too many items in the launch bar to fit the screen, it will
|
||||
become scrollable.</p>
|
||||
<h2>Terminology</h2>
|
||||
<ul>
|
||||
<li><strong>Launcher</strong>: a button that can be (or is) displayed on the
|
||||
@@ -12,16 +15,16 @@
|
||||
<li><strong>Visible Launcher</strong>: a launcher that is currently displayed
|
||||
on the launch bar.</li>
|
||||
</ul>
|
||||
<h2>Configuring the Launch bar</h2>
|
||||
<h2>Configuring the desktop Launch bar</h2>
|
||||
<p>There are two ways to configure the launch bar:</p>
|
||||
<ul>
|
||||
<li>Right click in the empty space between launchers on the launch bar and
|
||||
select <em>Configure Launchbar.</em>
|
||||
</li>
|
||||
<li>Click on the <a href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
<li>Click on the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
select <em>Configure Launchbar</em>.</li>
|
||||
</ul>
|
||||
<p>This will open a new tab with the <a href="#root/_help_oPVyFC7WL2Lp">Note Tree</a> listing
|
||||
<p>This will open a new tab with the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a> listing
|
||||
the launchers.</p>
|
||||
<p>
|
||||
<img src="Launch Bar_image.png">
|
||||
@@ -29,6 +32,19 @@
|
||||
<p>Expanding <em>Available Launchers</em> section will show the list of launchers
|
||||
that are not displayed on the launch bar. The <em>Visible Launchers</em> will
|
||||
show the ones that are currently displayed.</p>
|
||||
<h2>Configuring the mobile launch bar</h2>
|
||||
<p>The launch bar on mobile uses a different configuration from the desktop
|
||||
one. The reasoning is that not all desktop icons are available on mobile,
|
||||
and fewer icons fit on a mobile screen.</p>
|
||||
<p>To configure the launch bar on mobile, go to <a class="reference-link"
|
||||
href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
select <em>Configure Launchbar</em>.</p>
|
||||
<p>The configure the mobile launch bar while on the desktop (especially useful
|
||||
to configure more complicated launchers such as scripts or custom widgets),
|
||||
go to <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a> →
|
||||
Advanced → Show Hidden Subtree and look for the <em>Mobile Launch Bar</em> section.
|
||||
While in the hidden subtree, it's also possible to drag launchers between
|
||||
the <em>Mobile Launch Bar</em> and (Desktop) <em>Launch Bar</em> sections.</p>
|
||||
<h3>Adding/removing and reordering launchers</h3>
|
||||
<p>To display a new launcher in the launch bar, first look for it in the <em>Available Launchers</em> section.
|
||||
Then right click it and select <em>Move to visible launchers</em>. It is
|
||||
@@ -36,13 +52,13 @@
|
||||
<p>Similarly, to remove it from the launch bar, simply look for it in <em>Visible Launchers</em> then
|
||||
right click it and select <em>Move to available launchers</em> or use drag-and-drop.</p>
|
||||
<p>Drag-and-drop the items in the tree in order to change their
|
||||
order. See <a href="#root/_help_oPVyFC7WL2Lp">Note Tree</a> for more
|
||||
interaction options, including using keyboard shortcuts.</p>
|
||||
order. See <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a> for
|
||||
more interaction options, including using keyboard shortcuts.</p>
|
||||
<h2>Customizing the launcher</h2>
|
||||
<ul>
|
||||
<li>The icon of a launcher can be changed just like a normal note. See
|
||||
<a
|
||||
href="#root/_help_p9kXRFAkwN4o">Note Icons</a> for more information.</li>
|
||||
class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/BFs8mudNFgCS/_help_p9kXRFAkwN4o">Note Icons</a> for more information.</li>
|
||||
<li>The title of the launcher can also be changed.</li>
|
||||
</ul>
|
||||
<h3>Resetting</h3>
|
||||
@@ -54,42 +70,39 @@
|
||||
<p>Right click either the <em>Available launchers</em> or <em>Visible launchers</em> sections
|
||||
and select one of the options:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Note Launcher</strong>
|
||||
<br>A note launcher will simply navigate to a specified note.</p>
|
||||
<li><strong>Note Launcher</strong>
|
||||
<br>A note launcher will simply navigate to a specified note.
|
||||
<ol>
|
||||
<li>Set the <code spellcheck="false">target</code> promoted attribute to the
|
||||
note to navigate to.</li>
|
||||
<li>Optionally, set <code spellcheck="false">hoistedNote</code> to hoist a particular
|
||||
note. See <a href="#root/_help_OR8WJ7Iz9K4U">Note Hoisting</a> for
|
||||
note. See <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/wArbEsdSae6g/_help_OR8WJ7Iz9K4U">Note Hoisting</a> for
|
||||
more information.</li>
|
||||
<li>Optionally, set a <code spellcheck="false">keyboardShortcut</code> to trigger
|
||||
the launcher.</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Script Launcher</strong>
|
||||
<br>An advanced launcher which will run a script upon pressing. See
|
||||
<a
|
||||
href="#root/_help_CdNpE2pqjmI6">Scripts</a> for more information.</p>
|
||||
<ol>
|
||||
<li>Set <code spellcheck="false">script</code> to point to the desired script
|
||||
to run.</li>
|
||||
<li>Optionally, set a <code spellcheck="false">keyboardShortcut</code> to trigger
|
||||
the launcher.</li>
|
||||
</ol>
|
||||
<li><strong>Script Launcher</strong>
|
||||
<br>An advanced launcher which will run a script upon pressing. See
|
||||
<a
|
||||
class="reference-link" href="#root/pOsGYCXsbNQG/_help_CdNpE2pqjmI6">Scripting</a> for more information.
|
||||
<ol>
|
||||
<li>Set <code spellcheck="false">script</code> to point to the desired script
|
||||
to run.</li>
|
||||
<li>Optionally, set a <code spellcheck="false">keyboardShortcut</code> to trigger
|
||||
the launcher.</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>
|
||||
<li class="ck-list-marker-bold">
|
||||
<p><strong>Custom Widget</strong>
|
||||
</p>
|
||||
<p>Allows defining a custom widget to be rendered inside the launcher. See
|
||||
<a
|
||||
href="#root/_help_SynTBQiBsdYJ">Widget Basics</a> for more information.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Spacers</strong>
|
||||
<br>Launchers that create some distance between other launchers for better
|
||||
visual distinction.</p>
|
||||
class="reference-link" href="#root/pOsGYCXsbNQG/CdNpE2pqjmI6/yIhgI5H7A2Sm/MgibgPcfeuGz/_help_SynTBQiBsdYJ">Widget Basics</a> for more information.</p>
|
||||
</li>
|
||||
<li><strong>Spacers</strong>
|
||||
<br>Launchers that create some distance between other launchers for better
|
||||
visual distinction.</li>
|
||||
</ol>
|
||||
<p>Launchers are configured via predefined <a href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a>.</p>
|
||||
<p>Launchers are configured via predefined <a class="reference-link"
|
||||
href="#root/pOsGYCXsbNQG/tC7s2alapj8V/zEY4DaJG4YT5/_help_OFXdgB2nNk1F">Promoted Attributes</a>.</p>
|
||||
@@ -4,8 +4,7 @@ class="image image-style-align-center">
|
||||
<img style="aspect-ratio:1398/1015;" src="Split View_2_Split View_im.png"
|
||||
width="1398" height="1015">
|
||||
</figure>
|
||||
|
||||
<h2><strong>Interactions</strong></h2>
|
||||
<h2><strong>Interactions</strong></h2>
|
||||
<ul>
|
||||
<li>Press the
|
||||
<img src="Split View_Split View_imag.png">button to the right of a note's title to open a new split to the right
|
||||
@@ -51,11 +50,12 @@ class="image image-style-align-center">
|
||||
<ul>
|
||||
<li>On smartphones, the split views are laid out vertically (one on the top
|
||||
and one on the bottom), instead of horizontally as on the desktop.</li>
|
||||
<li>There can be only one split open per tab.</li>
|
||||
<li>It's not possible to resize the two split panes.</li>
|
||||
<li>When the keyboard is opened, the active note will be “maximized”, thus
|
||||
allowing for more space even when a split is open. When the keyboard is
|
||||
closed, the splits become equal in size again.</li>
|
||||
<li
|
||||
>There can be only one split open per tab.</li>
|
||||
<li>It's not possible to resize the two split panes.</li>
|
||||
<li>When the keyboard is opened, the active note will be “maximized”, thus
|
||||
allowing for more space even when a split is open. When the keyboard is
|
||||
closed, the splits become equal in size again.</li>
|
||||
</ul>
|
||||
<p>Interaction:</p>
|
||||
<ul>
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
<ul>
|
||||
<li>For the vertical layout, the tabs will be placed at the top but to the
|
||||
right of the <a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</li>
|
||||
<li>For the horizontal layout, the tabs will be placed at the top in full-width,
|
||||
<li
|
||||
>For the horizontal layout, the tabs will be placed at the top in full-width,
|
||||
above the <a href="#root/_help_oPVyFC7WL2Lp">note tree</a>, allowing for
|
||||
more tabs to be comfortably displayed.</li>
|
||||
</ul>
|
||||
@@ -22,7 +23,8 @@
|
||||
href="#root/_help_luNhaphA37EO">Split View</a>. Each tab can have one or more
|
||||
notes, displayed horizontally.</li>
|
||||
<li>Tabs can be reordered by drag-and-dropping it into a new position.</li>
|
||||
<li>An existing tab can be displayed in a new window by dragging the tab upwards
|
||||
<li
|
||||
>An existing tab can be displayed in a new window by dragging the tab upwards
|
||||
or downwards. It is not possible to combine tabs back into another window.</li>
|
||||
</ul>
|
||||
<h2>Keyboard interaction</h2>
|
||||
@@ -36,7 +38,29 @@
|
||||
<li><kbd>Ctrl</kbd>+<kbd>Tab</kbd> and <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Tab</kbd> to
|
||||
go to the next or previous tab.</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>1</kbd>, <kbd>Ctrl</kbd>+<kbd>2</kbd>, up to <kbd>Ctrl</kbd>+<kbd>9</kbd> to
|
||||
activate the first, second and up til ninth tab.</li>
|
||||
activate the first, second and up to ninth tab.</li>
|
||||
<li>There is also a shortcut to go to the last tab, but it is not assigned
|
||||
a key by default.</li>
|
||||
</ul>
|
||||
</ul>
|
||||
<h2>Mobile</h2>
|
||||
<figure class="image image-style-align-right image_resized" style="width:34.12%;">
|
||||
<img style="aspect-ratio:1242/2688;" src="Tabs_IMG_1767.PNG"
|
||||
width="1242" height="2688">
|
||||
</figure>
|
||||
<p>Tabs are also supported on the <a class="reference-link" href="#root/pOsGYCXsbNQG/Otzi9La2YAUX/_help_RDslemsQ6gCp">Mobile Frontend</a>.</p>
|
||||
<p>Since v0.102.0, the tabs are displayed by pressing the dedicated tab switcher
|
||||
button in the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>.
|
||||
In this view the tabs are laid out on a grid with a preview of the note
|
||||
content.</p>
|
||||
<p>The context menu button at the top-right of the popup allows creating
|
||||
a new tab, reopening the last closed tab and closing all the tabs.</p>
|
||||
<p><a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_luNhaphA37EO">Split View</a>s
|
||||
are also indicated in the tab switcher, with two titles displayed in a
|
||||
tab.</p>
|
||||
<aside class="admonition note">
|
||||
<p>Versions prior to v0.102.0 also supported tabs, but they were displayed
|
||||
directly above the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>.
|
||||
The decision to use a more mobile-like tab switcher was taken because the
|
||||
original tab bar could not support many tabs at once and the new design
|
||||
better aligns with how mobile applications handle tabs.</p>
|
||||
</aside>
|
||||
BIN
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs_IMG_1767.PNG
generated
vendored
Normal file
|
After Width: | Height: | Size: 195 KiB |
@@ -1,35 +1,168 @@
|
||||
<p>Trilium (<a href="#root/_help_WOcw2SLH6tbX">server edition</a>) has a mobile
|
||||
web frontend which is optimized for touch based devices - smartphones and
|
||||
tablets. It is activated automatically during login process based on browser
|
||||
detection.</p>
|
||||
<p>Mobile frontend is limited in features compared to full desktop frontend.
|
||||
<figure class="image image_resized image-style-align-right" style="width:33.52%;">
|
||||
<img style="aspect-ratio:1242/2688;" src="Mobile Frontend_IMG_1765.PNG"
|
||||
width="1242" height="2688">
|
||||
</figure>
|
||||
<p>Trilium has a mobile web frontend which is optimized for touch based devices
|
||||
- smartphones and tablets. It is activated automatically during login process
|
||||
based on browser detection.</p>
|
||||
<p>Mobile frontend is limited in features compared to the full desktop version.
|
||||
See below for more details on this.</p>
|
||||
<p>Note that this is not an Android/iOS app, this is just mobile friendly
|
||||
web page served on the <a href="#root/_help_WOcw2SLH6tbX">server edition</a>.</p>
|
||||
<h2>Layout basics</h2>
|
||||
<p>Unlike the desktop version, the mobile version has a slightly different
|
||||
UI meant to better fit the constrained screens of a mobile phone.</p>
|
||||
<p>Here is a non-exhaustive list of differences between the desktop version
|
||||
and the mobile one:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a> is
|
||||
displayed as a sidebar. To display the sidebar, press the button in the
|
||||
top-left of the screen.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>There is also a swipe gesture that can be done from the left of the screen,
|
||||
but the browser's navigation gesture interferes with it most of the time
|
||||
(depending on the platform).</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Press and hold a note to display the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/oPVyFC7WL2Lp/_help_YtSN43OrfzaA">Note tree contextual menu</a>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>The <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/wArbEsdSae6g/_help_Ms1nauBra7gq">Quick search</a> bar
|
||||
is also displayed at the top of the note tree.</li>
|
||||
<li>The full <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/wArbEsdSae6g/_help_eIg8jdvaoNNd">Search</a> function
|
||||
can be triggered either from either the <a class="reference-link"
|
||||
href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a> or
|
||||
from the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>,
|
||||
if configured.</li>
|
||||
<li>
|
||||
<p>The <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a> is
|
||||
displayed at the bottom of the screen.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>The launch bar uses a different configuration for icons than the desktop
|
||||
version. See the dedicated page for more information on how to configure
|
||||
it.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Most of the note-related actions are grouped in the horizontal dots icon
|
||||
on the top-right of the note.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_3seOhtN8uLIY">Tabs</a> are
|
||||
grouped under a tab switcher in the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a>,
|
||||
where the tabs are displayed in a full-screen grid with preview for easy
|
||||
switching, as well as additional options such as reopening closed tabs.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Since v0.100.0, <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_luNhaphA37EO">Split View</a> can
|
||||
also be used in mobile view, but with a maximum of two panes at once. The
|
||||
splits are displayed vertically instead of horizontally.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Starting with v0.102.0, the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_IjZS7iK5EXtb">New Layout</a> is
|
||||
enforced on mobile. This brings features such as the note badges, note
|
||||
type switcher or collection properties which would otherwise not be available.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>Installing as a PWA</h2>
|
||||
<p>The mobile view can be set up as a PWA. While this does not offer any
|
||||
offline capabilities, it will display the application in full-screen and
|
||||
makes it easy to access via your mobile phone's home screen.</p>
|
||||
<h3>On iOS with Safari</h3>
|
||||
<ol>
|
||||
<li>Open your default web browser and access your Trilium instance.</li>
|
||||
<li
|
||||
>Login.</li>
|
||||
<li>
|
||||
<p>Press the […] button in the bottom-right of the screen and select Share.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Scroll down to reveal the full list of items and choose “Add to Home Screen”.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Press “Add” and the web app will be available.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<h3>On Android with Google Chrome</h3>
|
||||
<aside class="admonition important">
|
||||
<p>Google Chrome requires the server to be served over HTTPS in order to
|
||||
display in full-screen. If using HTTP, the app will appear like a normal
|
||||
web page (similar to a bookmark).</p>
|
||||
</aside>
|
||||
<ol>
|
||||
<li>Open your default web browser and access your Trilium instance.</li>
|
||||
<li
|
||||
>Login.</li>
|
||||
<li>
|
||||
<p>Press the three vertical dots icon in the top-right of the screen and
|
||||
select <em>Add to Home screen.</em>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Select the <em>Install</em> option.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Select an appropriate name.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The web app will appear as an application, not on the home screen.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<h3>On Android with Brave</h3>
|
||||
<aside class="admonition important">
|
||||
<p>Brave requires the server to be served over HTTPS in order to display
|
||||
in full-screen. If using HTTP, the app will appear like a normal web page
|
||||
(similar to a bookmark).</p>
|
||||
</aside>
|
||||
<ol>
|
||||
<li>Open your default web browser and access your Trilium instance.</li>
|
||||
<li
|
||||
>Login.</li>
|
||||
<li>
|
||||
<p>Press the three vertical dots icon in the bottom-right of the screen and
|
||||
select <em>Add to Home screen</em>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Press the <em>Install</em> option.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The web app will appear as an application, not on the home screen.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<h3>On Samsung Browser</h3>
|
||||
<ol>
|
||||
<li>Open your default web browser and access your Trilium instance.</li>
|
||||
<li
|
||||
>Login.</li>
|
||||
<li>
|
||||
<p>Press the hamburger menu in the bottom-right of the screen.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Select <em>Add to</em>, followed by <em>Home screen</em>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Press <em>Add</em> and the web app will appear on the home page.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<h2>Testing via the desktop application</h2>
|
||||
<p>If you are running Trilium without a dedicated <a href="#root/_help_WOcw2SLH6tbX">server installation</a>,
|
||||
you can still test the mobile application using the desktop application.
|
||||
For more information, see <a class="reference-link" href="#root/_help_nRqcgfTb97uV">Using the desktop application as a server</a>.
|
||||
To access it go to <code spellcheck="false">http://<ip>:37840/login?mobile</code> .</p>
|
||||
<h2>Limitations</h2>
|
||||
<p>Mobile frontend provides only some of the features of the full desktop
|
||||
frontend:</p>
|
||||
<ul>
|
||||
<li>it is possible to browse the whole note tree, read and edit all types
|
||||
of notes, but you can create only text notes</li>
|
||||
<li>reading and editing <a href="#root/_help_bwg0e8ewQMak">protected notes</a> is
|
||||
possible, but creating them is not supported</li>
|
||||
<li>editing options is not supported</li>
|
||||
<li>cloning notes is not supported</li>
|
||||
<li>uploading file attachments is not supported</li>
|
||||
</ul>
|
||||
<h2>Forcing mobile/desktop frontend</h2>
|
||||
<p>Trilium decides automatically whether to use mobile or desktop frontend.
|
||||
<p>Trilium decides automatically whether to use mobile or desktop front-end.
|
||||
If this is not appropriate, you can use <code spellcheck="false">?mobile</code> or
|
||||
<code
|
||||
spellcheck="false">?desktop</code>query param on <strong>login</strong> page (Note: you might
|
||||
need to log out).</p>
|
||||
<p>Alternatively, simply select <em>Switch to Mobile/Desktop Version</em> in
|
||||
the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_x3i7MxGccDuM">Global menu</a>.</p>
|
||||
<h2>Scripting</h2>
|
||||
<p>You can alter the behavior with <a href="#root/_help_CdNpE2pqjmI6">scripts</a> just
|
||||
like for normal frontend. For script notes to be executed, they need to
|
||||
have labeled <code spellcheck="false">#run=mobileStartup</code>.</p>
|
||||
<p>You can alter the behavior with <a class="reference-link" href="#root/pOsGYCXsbNQG/_help_CdNpE2pqjmI6">Scripting</a>,
|
||||
just like for normal frontend. For script notes to be executed, they need
|
||||
to have labeled <code spellcheck="false">#run=mobileStartup</code>.</p>
|
||||
<p>Custom <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_xYmIYSP6wE3F">Launch Bar</a> widgets
|
||||
are also supported.</p>
|
||||
BIN
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Mobile Frontend_IMG_1765.PNG
generated
vendored
Normal file
|
After Width: | Height: | Size: 280 KiB |
@@ -40,18 +40,24 @@ export default function buildLaunchBarConfig() {
|
||||
type: "launcher",
|
||||
command: "showRecentChanges",
|
||||
icon: "bx bx-history"
|
||||
}
|
||||
},
|
||||
searchNotes: {
|
||||
title: t("hidden-subtree.search-notes-title"),
|
||||
type: "launcher",
|
||||
command: "searchNotes",
|
||||
icon: "bx bx-search",
|
||||
},
|
||||
bookmarks: {
|
||||
title: t("hidden-subtree.bookmarks-title"),
|
||||
type: "launcher",
|
||||
builtinWidget: "bookmarks",
|
||||
icon: "bx bx-bookmark"
|
||||
},
|
||||
};
|
||||
|
||||
const desktopAvailableLaunchers: HiddenSubtreeItem[] = [
|
||||
{
|
||||
id: "_lbBackInHistory",
|
||||
...sharedLaunchers.backInHistory
|
||||
},
|
||||
{
|
||||
id: "_lbForwardInHistory",
|
||||
...sharedLaunchers.forwardInHistory
|
||||
},
|
||||
{ id: "_lbBackInHistory", ...sharedLaunchers.backInHistory },
|
||||
{ id: "_lbForwardInHistory", ...sharedLaunchers.forwardInHistory },
|
||||
{
|
||||
id: "_commandPalette",
|
||||
title: t("hidden-subtree.command-palette"),
|
||||
@@ -82,11 +88,7 @@ export default function buildLaunchBarConfig() {
|
||||
},
|
||||
{
|
||||
id: "_lbSearch",
|
||||
title: t("hidden-subtree.search-notes-title"),
|
||||
type: "launcher",
|
||||
command: "searchNotes",
|
||||
icon: "bx bx-search",
|
||||
attributes: [{ type: "label", name: "desktopOnly" }]
|
||||
...sharedLaunchers.searchNotes
|
||||
},
|
||||
{
|
||||
id: "_lbJumpTo",
|
||||
@@ -128,13 +130,7 @@ export default function buildLaunchBarConfig() {
|
||||
baseSize: "50",
|
||||
growthFactor: "0"
|
||||
},
|
||||
{
|
||||
id: "_lbBookmarks",
|
||||
title: t("hidden-subtree.bookmarks-title"),
|
||||
type: "launcher",
|
||||
builtinWidget: "bookmarks",
|
||||
icon: "bx bx-bookmark"
|
||||
},
|
||||
{ id: "_lbBookmarks", ...sharedLaunchers.bookmarks },
|
||||
{
|
||||
id: "_lbToday",
|
||||
...sharedLaunchers.openToday
|
||||
@@ -179,22 +175,15 @@ export default function buildLaunchBarConfig() {
|
||||
|
||||
const mobileAvailableLaunchers: HiddenSubtreeItem[] = [
|
||||
{ id: "_lbMobileNewNote", ...sharedLaunchers.newNote },
|
||||
{ id: "_lbMobileSearchNotes", ...sharedLaunchers.searchNotes },
|
||||
{ id: "_lbMobileToday", ...sharedLaunchers.openToday },
|
||||
{
|
||||
id: "_lbMobileRecentChanges",
|
||||
...sharedLaunchers.recentChanges
|
||||
}
|
||||
{ id: "_lbMobileRecentChanges", ...sharedLaunchers.recentChanges },
|
||||
{ id: "_lbMobileBookmarks", ...sharedLaunchers.bookmarks }
|
||||
];
|
||||
|
||||
const mobileVisibleLaunchers: HiddenSubtreeItem[] = [
|
||||
{
|
||||
id: "_lbMobileBackInHistory",
|
||||
...sharedLaunchers.backInHistory
|
||||
},
|
||||
{
|
||||
id: "_lbMobileForwardInHistory",
|
||||
...sharedLaunchers.forwardInHistory
|
||||
},
|
||||
{ id: "_lbMobileBackInHistory", ...sharedLaunchers.backInHistory },
|
||||
{ id: "_lbMobileForwardInHistory", ...sharedLaunchers.forwardInHistory },
|
||||
{
|
||||
id: "_lbMobileJumpTo",
|
||||
title: t("hidden-subtree.jump-to-note-title"),
|
||||
@@ -210,7 +199,8 @@ export default function buildLaunchBarConfig() {
|
||||
id: "_lbMobileTabSwitcher",
|
||||
title: t("hidden-subtree.tab-switcher-title"),
|
||||
type: "launcher",
|
||||
builtinWidget: "mobileTabSwitcher"
|
||||
builtinWidget: "mobileTabSwitcher",
|
||||
icon: "bx bx-rectangle"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Documentation
|
||||
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/y2NjgAW23yyj/Documentation_image.png" width="205" height="162">
|
||||
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/qIpo9UiTZNdm/Documentation_image.png" width="205" height="162">
|
||||
|
||||
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>.
|
||||
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.
|
||||
|
||||
248
docs/User Guide/!!!meta.json
vendored
@@ -1491,20 +1491,6 @@
|
||||
"isInheritable": false,
|
||||
"position": 20
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "bwg0e8ewQMak",
|
||||
"isInheritable": false,
|
||||
"position": 30
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "CdNpE2pqjmI6",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"name": "shareAlias",
|
||||
@@ -1518,11 +1504,90 @@
|
||||
"value": "bx bx-mobile-alt",
|
||||
"isInheritable": false,
|
||||
"position": 60
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "x3i7MxGccDuM",
|
||||
"isInheritable": false,
|
||||
"position": 70
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "IjZS7iK5EXtb",
|
||||
"isInheritable": false,
|
||||
"position": 80
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "oPVyFC7WL2Lp",
|
||||
"isInheritable": false,
|
||||
"position": 90
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "YtSN43OrfzaA",
|
||||
"isInheritable": false,
|
||||
"position": 110
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "xYmIYSP6wE3F",
|
||||
"isInheritable": false,
|
||||
"position": 120
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "Ms1nauBra7gq",
|
||||
"isInheritable": false,
|
||||
"position": 130
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "eIg8jdvaoNNd",
|
||||
"isInheritable": false,
|
||||
"position": 140
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "3seOhtN8uLIY",
|
||||
"isInheritable": false,
|
||||
"position": 150
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "CdNpE2pqjmI6",
|
||||
"isInheritable": false,
|
||||
"position": 160
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "luNhaphA37EO",
|
||||
"isInheritable": false,
|
||||
"position": 170
|
||||
}
|
||||
],
|
||||
"format": "markdown",
|
||||
"dataFileName": "Mobile Frontend.md",
|
||||
"attachments": []
|
||||
"attachments": [
|
||||
{
|
||||
"attachmentId": "JRliU5bOLszn",
|
||||
"title": "IMG_1765.PNG",
|
||||
"role": "image",
|
||||
"mime": "image/png",
|
||||
"position": 10,
|
||||
"dataFileName": "Mobile Frontend_IMG_1765.PNG"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"isClone": false,
|
||||
@@ -2793,6 +2858,20 @@
|
||||
"value": "tabs",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "RDslemsQ6gCp",
|
||||
"isInheritable": false,
|
||||
"position": 50
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "xYmIYSP6wE3F",
|
||||
"isInheritable": false,
|
||||
"position": 60
|
||||
}
|
||||
],
|
||||
"format": "markdown",
|
||||
@@ -2821,6 +2900,14 @@
|
||||
"mime": "image/png",
|
||||
"position": 10,
|
||||
"dataFileName": "2_Tabs_image.png"
|
||||
},
|
||||
{
|
||||
"attachmentId": "rFdKVhoVtsLA",
|
||||
"title": "IMG_1767.PNG",
|
||||
"role": "image",
|
||||
"mime": "image/png",
|
||||
"position": 10,
|
||||
"dataFileName": "Tabs_IMG_1767.PNG"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2840,20 +2927,6 @@
|
||||
"type": "text",
|
||||
"mime": "text/html",
|
||||
"attributes": [
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "x0JgW8UqGXvq",
|
||||
"isInheritable": false,
|
||||
"position": 10
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "x3i7MxGccDuM",
|
||||
"isInheritable": false,
|
||||
"position": 20
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
@@ -2861,41 +2934,6 @@
|
||||
"isInheritable": false,
|
||||
"position": 30
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "p9kXRFAkwN4o",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "OR8WJ7Iz9K4U",
|
||||
"isInheritable": false,
|
||||
"position": 50
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "CdNpE2pqjmI6",
|
||||
"isInheritable": false,
|
||||
"position": 60
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "SynTBQiBsdYJ",
|
||||
"isInheritable": false,
|
||||
"position": 70
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "OFXdgB2nNk1F",
|
||||
"isInheritable": false,
|
||||
"position": 80
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"name": "iconClass",
|
||||
@@ -2909,6 +2947,55 @@
|
||||
"value": "launch-bar",
|
||||
"isInheritable": false,
|
||||
"position": 90
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "OFXdgB2nNk1F",
|
||||
"isInheritable": false,
|
||||
"position": 100
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "SynTBQiBsdYJ",
|
||||
"isInheritable": false,
|
||||
"position": 110
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "CdNpE2pqjmI6",
|
||||
"isInheritable": false,
|
||||
"position": 120
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "OR8WJ7Iz9K4U",
|
||||
"isInheritable": false,
|
||||
"position": 130
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "p9kXRFAkwN4o",
|
||||
"isInheritable": false,
|
||||
"position": 140
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "x3i7MxGccDuM",
|
||||
"isInheritable": false,
|
||||
"position": 150
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "x0JgW8UqGXvq",
|
||||
"isInheritable": false,
|
||||
"position": 160
|
||||
}
|
||||
],
|
||||
"format": "markdown",
|
||||
@@ -5256,28 +5343,25 @@
|
||||
"value": "bx bx-bookmarks",
|
||||
"isInheritable": false,
|
||||
"position": 30
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "xYmIYSP6wE3F",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "x3i7MxGccDuM",
|
||||
"isInheritable": false,
|
||||
"position": 50
|
||||
}
|
||||
],
|
||||
"format": "markdown",
|
||||
"dataFileName": "Bookmarks.md",
|
||||
"attachments": [
|
||||
{
|
||||
"attachmentId": "99dD0P74W8QJ",
|
||||
"title": "bookmark-folder.png",
|
||||
"role": "image",
|
||||
"mime": "image/png",
|
||||
"position": 10,
|
||||
"dataFileName": "Bookmarks_bookmark-folder.png"
|
||||
},
|
||||
{
|
||||
"attachmentId": "9dX71aLlSl9R",
|
||||
"title": "bookmarks.gif",
|
||||
"role": "image",
|
||||
"mime": "image/gif",
|
||||
"position": 10,
|
||||
"dataFileName": "Bookmarks_bookmarks.gif"
|
||||
}
|
||||
]
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"isClone": false,
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
# Bookmarks
|
||||
To easily access selected notes, you can bookmark them. See demo:
|
||||
Frequently used notes can be bookmarked, which will make them appear in the <a class="reference-link" href="../UI%20Elements/Launch%20Bar.md">Launch Bar</a> for easy access.
|
||||
|
||||

|
||||
## Configuring the launch bar
|
||||
|
||||
If bookmarks don't appear in the launch bar, then most likely the bookmark section has been hidden. Go to the <a class="reference-link" href="../UI%20Elements/Launch%20Bar.md">Launch Bar</a> configuration from the <a class="reference-link" href="../UI%20Elements/Global%20menu.md">Global menu</a> and ensure _Bookmarks_ is in the _Visible Launchers_ section.
|
||||
|
||||
## Bookmark folder
|
||||
|
||||
Space in the left panel is limited, and you might want to bookmark many items. One possible solution is to bookmark a folder, so it shows its children:
|
||||
|
||||

|
||||
To do this, bookmark a folder and assign it the `#bookmarkFolder` label.
|
||||
|
||||
To do this, you need to add a `#bookmarkFolder` label to the note.
|
||||
## Mobile
|
||||
|
||||
On mobile, bookmarks are only displayed starting with v0.102.0. Because of the more constrained screen size, the bookmarks are grouped under a single icon instead of displaying them as separate icons.
|
||||
|
||||
When pressed, a menu will appear listing all the bookmarks. Bookmark folders are also supported and will appear as sub-menus.
|
||||
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 305 KiB |
@@ -1,7 +1,11 @@
|
||||
# Launch Bar
|
||||
## Position of the Launch bar
|
||||
|
||||
Depending on the layout selected, the launcher bar will either be on the left side of the screen with buttons displayed vertically or at the top of the screen. See [Vertical and horizontal layout](Vertical%20and%20horizontal%20layout.md) for more information.
|
||||
On desktop, depending on the layout selected, the launcher bar will either be on the left side of the screen with buttons displayed vertically or at the top of the screen. See <a class="reference-link" href="Vertical%20and%20horizontal%20layout.md">Vertical and horizontal layout</a> for more information.
|
||||
|
||||
On mobile, the launch bar will always be at the bottom.
|
||||
|
||||
If there are too many items in the launch bar to fit the screen, it will become scrollable.
|
||||
|
||||
## Terminology
|
||||
|
||||
@@ -9,30 +13,38 @@ Depending on the layout selected, the launcher bar will either be on the left si
|
||||
* **Available Launcher**: a launcher that is not displayed on the launch bar, but can be added.
|
||||
* **Visible Launcher**: a launcher that is currently displayed on the launch bar.
|
||||
|
||||
## Configuring the Launch bar
|
||||
## Configuring the desktop Launch bar
|
||||
|
||||
There are two ways to configure the launch bar:
|
||||
|
||||
* Right click in the empty space between launchers on the launch bar and select _Configure Launchbar._
|
||||
* Click on the [Global menu](Global%20menu.md) and select _Configure Launchbar_.
|
||||
* Click on the <a class="reference-link" href="Global%20menu.md">Global menu</a> and select _Configure Launchbar_.
|
||||
|
||||
This will open a new tab with the [Note Tree](Note%20Tree.md) listing the launchers.
|
||||
This will open a new tab with the <a class="reference-link" href="Note%20Tree.md">Note Tree</a> listing the launchers.
|
||||
|
||||

|
||||
|
||||
Expanding _Available Launchers_ section will show the list of launchers that are not displayed on the launch bar. The _Visible Launchers_ will show the ones that are currently displayed.
|
||||
|
||||
## Configuring the mobile launch bar
|
||||
|
||||
The launch bar on mobile uses a different configuration from the desktop one. The reasoning is that not all desktop icons are available on mobile, and fewer icons fit on a mobile screen.
|
||||
|
||||
To configure the launch bar on mobile, go to <a class="reference-link" href="Global%20menu.md">Global menu</a> and select _Configure Launchbar_.
|
||||
|
||||
The configure the mobile launch bar while on the desktop (especially useful to configure more complicated launchers such as scripts or custom widgets), go to <a class="reference-link" href="Global%20menu.md">Global menu</a> → Advanced → Show Hidden Subtree and look for the _Mobile Launch Bar_ section. While in the hidden subtree, it's also possible to drag launchers between the _Mobile Launch Bar_ and (Desktop) _Launch Bar_ sections.
|
||||
|
||||
### Adding/removing and reordering launchers
|
||||
|
||||
To display a new launcher in the launch bar, first look for it in the _Available Launchers_ section. Then right click it and select _Move to visible launchers_. It is also possible to drag and drop the item manually.
|
||||
|
||||
Similarly, to remove it from the launch bar, simply look for it in _Visible Launchers_ then right click it and select _Move to available launchers_ or use drag-and-drop.
|
||||
|
||||
Drag-and-drop the items in the tree in order to change their order. See [Note Tree](Note%20Tree.md) for more interaction options, including using keyboard shortcuts.
|
||||
Drag-and-drop the items in the tree in order to change their order. See <a class="reference-link" href="Note%20Tree.md">Note Tree</a> for more interaction options, including using keyboard shortcuts.
|
||||
|
||||
## Customizing the launcher
|
||||
|
||||
* The icon of a launcher can be changed just like a normal note. See [Note Icons](../Notes/Note%20Icons.md) for more information.
|
||||
* The icon of a launcher can be changed just like a normal note. See <a class="reference-link" href="../Notes/Note%20Icons.md">Note Icons</a> for more information.
|
||||
* The title of the launcher can also be changed.
|
||||
|
||||
### Resetting
|
||||
@@ -45,19 +57,17 @@ Right click either the _Available launchers_ or _Visible launchers_ sections and
|
||||
|
||||
1. **Note Launcher**
|
||||
A note launcher will simply navigate to a specified note.
|
||||
|
||||
1. Set the `target` promoted attribute to the note to navigate to.
|
||||
2. Optionally, set `hoistedNote` to hoist a particular note. See [Note Hoisting](../Navigation/Note%20Hoisting.md) for more information.
|
||||
2. Optionally, set `hoistedNote` to hoist a particular note. See <a class="reference-link" href="../Navigation/Note%20Hoisting.md">Note Hoisting</a> for more information.
|
||||
3. Optionally, set a `keyboardShortcut` to trigger the launcher.
|
||||
2. **Script Launcher**
|
||||
An advanced launcher which will run a script upon pressing. See [Scripts](../../Scripting.md) for more information.
|
||||
|
||||
An advanced launcher which will run a script upon pressing. See <a class="reference-link" href="../../Scripting.md">Scripting</a> for more information.
|
||||
1. Set `script` to point to the desired script to run.
|
||||
2. Optionally, set a `keyboardShortcut` to trigger the launcher.
|
||||
3. **Custom Widget**
|
||||
|
||||
Allows defining a custom widget to be rendered inside the launcher. See [Widget Basics](../../Scripting/Frontend%20Basics/Custom%20Widgets/Widget%20Basics.md) for more information.
|
||||
Allows defining a custom widget to be rendered inside the launcher. See <a class="reference-link" href="../../Scripting/Frontend%20Basics/Custom%20Widgets/Widget%20Basics.md">Widget Basics</a> for more information.
|
||||
4. **Spacers**
|
||||
Launchers that create some distance between other launchers for better visual distinction.
|
||||
|
||||
Launchers are configured via predefined [Promoted Attributes](../../Advanced%20Usage/Attributes/Promoted%20Attributes.md).
|
||||
Launchers are configured via predefined <a class="reference-link" href="../../Advanced%20Usage/Attributes/Promoted%20Attributes.md">Promoted Attributes</a>.
|
||||
@@ -26,5 +26,20 @@ Since tabs are a commonly used feature, there are multiple keyboard shortcuts th
|
||||
* <kbd>Ctrl</kbd>+<kbd>W</kbd> to close the current tab.
|
||||
* <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd> to reopen the last closed tab.
|
||||
* <kbd>Ctrl</kbd>+<kbd>Tab</kbd> and <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Tab</kbd> to go to the next or previous tab.
|
||||
* <kbd>Ctrl</kbd>+<kbd>1</kbd>, <kbd>Ctrl</kbd>+<kbd>2</kbd>, up to <kbd>Ctrl</kbd>+<kbd>9</kbd> to activate the first, second and up til ninth tab.
|
||||
* There is also a shortcut to go to the last tab, but it is not assigned a key by default.
|
||||
* <kbd>Ctrl</kbd>+<kbd>1</kbd>, <kbd>Ctrl</kbd>+<kbd>2</kbd>, up to <kbd>Ctrl</kbd>+<kbd>9</kbd> to activate the first, second and up to ninth tab.
|
||||
* There is also a shortcut to go to the last tab, but it is not assigned a key by default.
|
||||
|
||||
## Mobile
|
||||
|
||||
<figure class="image image-style-align-right image_resized" style="width:34.12%;"><img style="aspect-ratio:1242/2688;" src="Tabs_IMG_1767.PNG" width="1242" height="2688"></figure>
|
||||
|
||||
Tabs are also supported on the <a class="reference-link" href="../../Installation%20%26%20Setup/Mobile%20Frontend.md">Mobile Frontend</a>.
|
||||
|
||||
Since v0.102.0, the tabs are displayed by pressing the dedicated tab switcher button in the <a class="reference-link" href="Launch%20Bar.md">Launch Bar</a>. In this view the tabs are laid out on a grid with a preview of the note content.
|
||||
|
||||
The context menu button at the top-right of the popup allows creating a new tab, reopening the last closed tab and closing all the tabs.
|
||||
|
||||
<a class="reference-link" href="Split%20View.md">Split View</a>s are also indicated in the tab switcher, with two titles displayed in a tab.
|
||||
|
||||
> [!NOTE]
|
||||
> Versions prior to v0.102.0 also supported tabs, but they were displayed directly above the <a class="reference-link" href="Launch%20Bar.md">Launch Bar</a>. The decision to use a more mobile-like tab switcher was taken because the original tab bar could not support many tabs at once and the new design better aligns with how mobile applications handle tabs.
|
||||
BIN
docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs_IMG_1767.PNG
vendored
Normal file
|
After Width: | Height: | Size: 195 KiB |
@@ -1,28 +1,85 @@
|
||||
# Mobile Frontend
|
||||
Trilium ([server edition](Server%20Installation.md)) has a mobile web frontend which is optimized for touch based devices - smartphones and tablets. It is activated automatically during login process based on browser detection.
|
||||
<figure class="image image_resized image-style-align-right" style="width:33.52%;"><img style="aspect-ratio:1242/2688;" src="Mobile Frontend_IMG_1765.PNG" width="1242" height="2688"></figure>
|
||||
|
||||
Mobile frontend is limited in features compared to full desktop frontend. See below for more details on this.
|
||||
Trilium has a mobile web frontend which is optimized for touch based devices - smartphones and tablets. It is activated automatically during login process based on browser detection.
|
||||
|
||||
Note that this is not an Android/iOS app, this is just mobile friendly web page served on the [server edition](Server%20Installation.md).
|
||||
Mobile frontend is limited in features compared to the full desktop version. See below for more details on this.
|
||||
|
||||
## Layout basics
|
||||
|
||||
Unlike the desktop version, the mobile version has a slightly different UI meant to better fit the constrained screens of a mobile phone.
|
||||
|
||||
Here is a non-exhaustive list of differences between the desktop version and the mobile one:
|
||||
|
||||
* The <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Note%20Tree.md">Note Tree</a> is displayed as a sidebar. To display the sidebar, press the button in the top-left of the screen.
|
||||
|
||||
* There is also a swipe gesture that can be done from the left of the screen, but the browser's navigation gesture interferes with it most of the time (depending on the platform).
|
||||
* Press and hold a note to display the <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Note%20Tree/Note%20tree%20contextual%20menu.md">Note tree contextual menu</a>.
|
||||
* The <a class="reference-link" href="../Basic%20Concepts%20and%20Features/Navigation/Quick%20search.md">Quick search</a> bar is also displayed at the top of the note tree.
|
||||
* The full <a class="reference-link" href="../Basic%20Concepts%20and%20Features/Navigation/Search.md">Search</a> function can be triggered either from either the <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Global%20menu.md">Global menu</a> or from the <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Launch%20Bar.md">Launch Bar</a>, if configured.
|
||||
* The <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Launch%20Bar.md">Launch Bar</a> is displayed at the bottom of the screen.
|
||||
|
||||
* The launch bar uses a different configuration for icons than the desktop version. See the dedicated page for more information on how to configure it.
|
||||
* Most of the note-related actions are grouped in the horizontal dots icon on the top-right of the note.
|
||||
* The <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Tabs.md">Tabs</a> are grouped under a tab switcher in the <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Launch%20Bar.md">Launch Bar</a>, where the tabs are displayed in a full-screen grid with preview for easy switching, as well as additional options such as reopening closed tabs.
|
||||
* Since v0.100.0, <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Split%20View.md">Split View</a> can also be used in mobile view, but with a maximum of two panes at once. The splits are displayed vertically instead of horizontally.
|
||||
* Starting with v0.102.0, the <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/New%20Layout.md">New Layout</a> is enforced on mobile. This brings features such as the note badges, note type switcher or collection properties which would otherwise not be available.
|
||||
|
||||
## Installing as a PWA
|
||||
|
||||
The mobile view can be set up as a PWA. While this does not offer any offline capabilities, it will display the application in full-screen and makes it easy to access via your mobile phone's home screen.
|
||||
|
||||
### On iOS with Safari
|
||||
|
||||
1. Open your default web browser and access your Trilium instance.
|
||||
2. Login.
|
||||
3. Press the \[…\] button in the bottom-right of the screen and select Share.
|
||||
4. Scroll down to reveal the full list of items and choose “Add to Home Screen”.
|
||||
5. Press “Add” and the web app will be available.
|
||||
|
||||
### On Android with Google Chrome
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Google Chrome requires the server to be served over HTTPS in order to display in full-screen. If using HTTP, the app will appear like a normal web page (similar to a bookmark).
|
||||
|
||||
1. Open your default web browser and access your Trilium instance.
|
||||
2. Login.
|
||||
3. Press the three vertical dots icon in the top-right of the screen and select _Add to Home screen._
|
||||
4. Select the _Install_ option.
|
||||
5. Select an appropriate name.
|
||||
6. The web app will appear as an application, not on the home screen.
|
||||
|
||||
### On Android with Brave
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Brave requires the server to be served over HTTPS in order to display in full-screen. If using HTTP, the app will appear like a normal web page (similar to a bookmark).
|
||||
|
||||
1. Open your default web browser and access your Trilium instance.
|
||||
2. Login.
|
||||
3. Press the three vertical dots icon in the bottom-right of the screen and select _Add to Home screen_.
|
||||
4. Press the _Install_ option.
|
||||
5. The web app will appear as an application, not on the home screen.
|
||||
|
||||
### On Samsung Browser
|
||||
|
||||
1. Open your default web browser and access your Trilium instance.
|
||||
2. Login.
|
||||
3. Press the hamburger menu in the bottom-right of the screen.
|
||||
4. Select _Add to_, followed by _Home screen_.
|
||||
5. Press _Add_ and the web app will appear on the home page.
|
||||
|
||||
## Testing via the desktop application
|
||||
|
||||
If you are running Trilium without a dedicated [server installation](Server%20Installation.md), you can still test the mobile application using the desktop application. For more information, see <a class="reference-link" href="Desktop%20Installation/Using%20the%20desktop%20application%20.md">Using the desktop application as a server</a>. To access it go to `http://<ip>:37840/login?mobile` .
|
||||
|
||||
## Limitations
|
||||
|
||||
Mobile frontend provides only some of the features of the full desktop frontend:
|
||||
|
||||
* it is possible to browse the whole note tree, read and edit all types of notes, but you can create only text notes
|
||||
* reading and editing [protected notes](../Basic%20Concepts%20and%20Features/Notes/Protected%20Notes.md) is possible, but creating them is not supported
|
||||
* editing options is not supported
|
||||
* cloning notes is not supported
|
||||
* uploading file attachments is not supported
|
||||
|
||||
## Forcing mobile/desktop frontend
|
||||
|
||||
Trilium decides automatically whether to use mobile or desktop frontend. If this is not appropriate, you can use `?mobile` or `?desktop` query param on **login** page (Note: you might need to log out).
|
||||
Trilium decides automatically whether to use mobile or desktop front-end. If this is not appropriate, you can use `?mobile` or `?desktop` query param on **login** page (Note: you might need to log out).
|
||||
|
||||
Alternatively, simply select _Switch to Mobile/Desktop Version_ in the <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Global%20menu.md">Global menu</a>.
|
||||
|
||||
## Scripting
|
||||
|
||||
You can alter the behavior with [scripts](../Scripting.md) just like for normal frontend. For script notes to be executed, they need to have labeled `#run=mobileStartup`.
|
||||
You can alter the behavior with <a class="reference-link" href="../Scripting.md">Scripting</a>, just like for normal frontend. For script notes to be executed, they need to have labeled `#run=mobileStartup`.
|
||||
|
||||
Custom <a class="reference-link" href="../Basic%20Concepts%20and%20Features/UI%20Elements/Launch%20Bar.md">Launch Bar</a> widgets are also supported.
|
||||
BIN
docs/User Guide/User Guide/Installation & Setup/Mobile Frontend_IMG_1765.PNG
vendored
Normal file
|
After Width: | Height: | Size: 280 KiB |