Compare commits

..

3 Commits

Author SHA1 Message Date
Elian Doran
199bfc8a37 Adjust next theme development docu (#8616) 2026-02-04 21:50:36 +02:00
hulmgulm
e82ae762f0 More fixes 2026-02-04 20:48:36 +01:00
hulmgulm
f90cc9aff7 Adjust theme development docu 2026-02-04 20:39:58 +01:00
23 changed files with 184 additions and 337 deletions

View File

@@ -77,6 +77,7 @@ export default class MobileLayout {
.child(<NoteTitleActions />)
.child(<NoteDetail />)
.child(<NoteList media="screen" />)
.child(<StandaloneRibbonAdapter component={SearchDefinitionTab} />)
.child(<SearchResult />)
.child(<FilePropertiesWrapper />)
)

View File

@@ -410,7 +410,6 @@ body.desktop .tabulator-popup-container,
.dropdown-menu.static {
box-shadow: unset;
backdrop-filter: unset !important;
}
.dropend .dropdown-toggle::after {
@@ -1556,7 +1555,6 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
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 {

View File

@@ -57,12 +57,12 @@
height: 18px;
}
/*
/*
* SEARCH PAGE
*/
/* Button bar */
.search-definition-widget .search-setting-table .search-actions-container {
.search-definition-widget .search-setting-table tbody:last-child div {
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;
}
}

View File

@@ -662,8 +662,7 @@
"show-cheatsheet": "Show Cheatsheet",
"toggle-zen-mode": "Zen Mode",
"new-version-available": "New Update Available",
"download-update": "Get Version {{latestVersion}}",
"search_notes": "Search notes"
"download-update": "Get Version {{latestVersion}}"
},
"zen_mode": {
"button_exit": "Exit Zen Mode"
@@ -762,8 +761,7 @@
"note_revisions": "Note revisions",
"error_cannot_get_branch_id": "Cannot get branchId for notePath '{{notePath}}'",
"error_unrecognized_command": "Unrecognized command {{command}}",
"backlinks": "Backlinks",
"content_language_switcher": "Content language: {{language}}"
"backlinks": "Backlinks"
},
"note_icon": {
"change_note_icon": "Change note icon",
@@ -908,7 +906,6 @@
"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",

View File

@@ -12,7 +12,6 @@
.fixed-note-tree-container {
height: 60%;
border-bottom: 1px solid var(--main-border-color);
overflow: auto;
.tree-wrapper {
padding: 0;

View File

@@ -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 = note && noteContext?.hoistedNoteId === "_lbMobileRoot" && isMobile() && note.noteId.startsWith("_lbMobile");
const hasFixedTree = noteContext?.hoistedNoteId === "_lbMobileRoot" && isMobile();
const props: TypeWidgetProps = {
note: note!,
@@ -216,7 +216,7 @@ export default function NoteDetail() {
"fixed-tree": hasFixedTree
})}
>
{hasFixedTree && <FixedTree noteContext={noteContext} />}
{hasFixedTree && <FixedTree />}
{Object.entries(noteTypesToRender).map(([ itemType, Element ]) => {
return <NoteDetailWrapper
@@ -232,7 +232,8 @@ export default function NoteDetail() {
);
}
function FixedTree({ noteContext }: { noteContext: NoteContext }) {
function FixedTree() {
const { noteContext } = useNoteContext();
const [ treeEl ] = useLegacyWidget(() => new NoteTreeWidget(), { noteContext });
return <div class="fixed-note-tree-container">{treeEl}</div>;
}

View File

@@ -45,10 +45,6 @@ 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")} />
@@ -109,7 +105,7 @@ function BrowserOnlyOptions() {
function DevelopmentOptions({ dropStart }: { dropStart: boolean }) {
return <>
<FormListHeader text="Development Options" />
<FormListHeader text="Development Options"></FormListHeader>
<FormDropdownSubmenu icon="bx bx-test-tube" title="Experimental features" dropStart={dropStart}>
{experimentalFeatures.map((feature) => (
<ExperimentalFeatureToggle key={feature.id} experimentalFeature={feature as ExperimentalFeature} />

View File

@@ -18,12 +18,14 @@ 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> {
@@ -63,12 +65,8 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
}
#onMobileResize() {
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;
const currentViewportHeight = getViewportHeight();
const isKeyboardOpened = (currentViewportHeight < this.originalViewportHeight);
this.$widget.toggleClass("virtual-keyboard-opened", isKeyboardOpened);
}
@@ -117,3 +115,6 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
}
}
function getViewportHeight() {
return window.visualViewport?.height ?? window.innerHeight;
}

View File

@@ -50,9 +50,6 @@ body.experimental-feature-new-layout {
}
}
}
body.desktop .title-actions {
> .collapsible,
> .note-type-switcher {
padding-inline-start: calc(24px - var(--title-actions-padding-start));
@@ -60,4 +57,3 @@ body.experimental-feature-new-layout {
}
}
}

View File

@@ -58,6 +58,33 @@
.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 {
@@ -117,50 +144,16 @@
}
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;

View File

@@ -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 && setSimilarNotesShown(!similarNotesShown));
useTriliumEvent("toggleRibbonTabSimilarNotes", () => setSimilarNotesShown(!similarNotesShown));
return (enabled &&
<StatusBarDropdown
@@ -242,8 +242,8 @@ export function NoteInfoBadge(context: NoteInfoContext) {
);
}
export function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdownRef }: Pick<NoteInfoContext, "note" | "setSimilarNotesShown"> & {
dropdownRef?: RefObject<BootstrapDropdown>;
function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdownRef }: NoteInfoContext & {
dropdownRef: RefObject<BootstrapDropdown>;
noteType: NoteType;
}) {
const { metadata, ...sizeProps } = useNoteMetadata(note);
@@ -251,7 +251,7 @@ export function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdown
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 @@ export function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdown
<NoteInfoValue text={t("note_info_widget.note_size")} title={t("note_info_widget.note_size_info")} value={<NoteSizeWidget {...sizeProps} />} />
</ul>
{setSimilarNotesShown && <LinkButton
<LinkButton
text={t("note_info_widget.show_similar_notes")}
onClick={() => {
dropdownRef?.current?.hide();
dropdownRef.current?.hide();
setSimilarNotesShown(true);
}}
/>}
</div>
/>
</>
);
}

View File

@@ -1,3 +0,0 @@
.code-note-switcher-modal .dropdown-menu {
background: none !important;
}

View File

@@ -1,24 +1,17 @@
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, FormDropdownSubmenu, FormListItem } from "../react/FormList";
import { useNoteContext, useNoteProperty } from "../react/hooks";
import { FormDropdownDivider, FormListItem } from "../react/FormList";
import { useNoteContext } 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);
@@ -27,9 +20,6 @@ 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");
@@ -90,11 +80,6 @@ export default function MobileDetailMenu() {
>{t("close_pane_button.close_this_pane")}</FormListItem>
</>}
<FormDropdownDivider />
{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 />
</>}
/>
) : (
@@ -109,46 +94,13 @@ 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 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) {
function BacklinksModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined, modalShown: boolean, setModalShown: (shown: boolean) => void }) {
return (
<Modal
className="backlinks-modal tn-backlinks-widget"
@@ -164,7 +116,7 @@ function BacklinksModal({ note, modalShown, setModalShown }: { note: FNote | nul
);
}
function NotePathsModal({ note, modalShown, notePath, sortedNotePaths, setModalShown }: { note: FNote | null | undefined, sortedNotePaths: NotePathRecord[] | undefined, notePath: string | null | undefined } & WithModal) {
function NotePathsModal({ note, modalShown, notePath, sortedNotePaths, setModalShown }: { note: FNote | null | undefined, modalShown: boolean, sortedNotePaths: NotePathRecord[] | undefined, notePath: string | null | undefined, setModalShown: (shown: boolean) => void }) {
return (
<Modal
className="note-paths-modal"
@@ -182,57 +134,3 @@ 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>
);
}

View File

@@ -1,12 +0,0 @@
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);
}

View File

@@ -1,4 +1,5 @@
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";
@@ -116,18 +117,19 @@ 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} />}
@@ -139,7 +141,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 (
<>
@@ -153,10 +155,8 @@ export function NoteTypeCodeNoteList({ currentMimeType, mimeTypes, changeNoteTyp
</FormListItem>
))}
{setModalShown && <>
<FormDropdownDivider />
<FormListItem icon="bx bx-cog" onClick={() => setModalShown(true)}>{t("basic_properties.configure_code_notes")}</FormListItem>
</>}
<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;
}

View File

@@ -2,7 +2,6 @@ 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";
@@ -16,13 +15,12 @@ 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, { SplitButton } from "../react/Button";
import Button 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/ResponseContainer";
import { TabContext } from "./ribbon-interface";
import { SEARCH_OPTIONS, SearchOption } from "./SearchDefinitionOptions";
@@ -86,31 +84,15 @@ 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">
<ResponsiveContainer
desktop={searchOptions?.availableOptions.map(({ icon, label, tooltip, attributeName, attributeType, defaultValue }, index) => (
<Button
key={index} 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 }, index) => (
<FormListItem
key={index} icon={icon}
description={tooltip}
onClick={() => attributes.setAttribute(note, attributeType, attributeName, defaultValue ?? "")}
>{label}</FormListItem>
))}
</Dropdown>
}
/>
{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 ?? "")}
/>
))}
<AddBulkActionButton note={note} />
</td>
@@ -131,7 +113,48 @@ export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabCon
})}
</tbody>
<BulkActionsList note={note} />
<SearchButtonBar note={note} refreshResults={refreshResults} />
<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>
</table>
)}
</div>
@@ -139,56 +162,6 @@ 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[]>();
@@ -221,18 +194,15 @@ 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 }, index) => (
<Fragment key={index}>
{ACTION_GROUPS.map(({ actions, title }) => (
<>
<FormListHeader text={title} />
<div>
{actions.map(({ actionName, actionTitle }) => (
<FormListItem key={actionName} onClick={() => bulk_action.addAction(note.noteId, actionName)}>{actionTitle}</FormListItem>
))}
</div>
</Fragment>
{actions.map(({ actionName, actionTitle }) => (
<FormListItem onClick={() => bulk_action.addAction(note.noteId, actionName)}>{actionTitle}</FormListItem>
))}
</>
))}
</Dropdown>
);

View File

@@ -60,7 +60,7 @@
</p>
<h2>Step 5. Making changes</h2>
<p>Simply go back to the note and change according to needs. To apply the
changes to the current window, press <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>R </kbd> to
changes to the current window, press <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>R</kbd> to
refresh.</p>
<p>It's a good idea to keep two windows, one for editing and the other one
for previewing the changes.</p>

View File

@@ -1,5 +1,5 @@
<aside class="admonition note">
<p>e This page describes how to create custom icon packs. For a general description
<p>This page describes how to create custom icon packs. For a general description
of how to use already existing icon packs, see&nbsp;<a class="reference-link"
href="#root/_help_gOKqSJgXLcIj">Icon Packs</a>.</p>
</aside>
@@ -74,9 +74,9 @@
"bx-ball": {
"glyph": "\ue9c2",
"terms": [ "ball" ]
},
},
"bxs-party": {
"glyph": "\uec92"
"glyph": "\uec92",
"terms": [ "party" ]
}
}

View File

@@ -18,7 +18,8 @@
</ul>
<h2>Overrides</h2>
<p>Do note that the TriliumNext theme has a few more overrides than the legacy
theme, so you might need to suffix <code spellcheck="false">!important</code> if
the style changes are not applied.</p><pre><code class="language-text-css">:root {
--launcher-pane-background-color: #0d6efd !important;
theme. Due to that, it is recommended to use <code spellcheck="false">#trilium-app</code> with
a next theme instead of the <code spellcheck="false">:root</code> of a legacy
theme.</p><pre><code class="language-text-css">#trilium-app {
--launcher-pane-background-color: #0d6efd;
}</code></pre>

View File

@@ -40,18 +40,18 @@ 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",
}
};
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,7 +82,11 @@ export default function buildLaunchBarConfig() {
},
{
id: "_lbSearch",
...sharedLaunchers.searchNotes
title: t("hidden-subtree.search-notes-title"),
type: "launcher",
command: "searchNotes",
icon: "bx bx-search",
attributes: [{ type: "label", name: "desktopOnly" }]
},
{
id: "_lbJumpTo",
@@ -175,14 +179,22 @@ 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
}
];
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"),
@@ -198,8 +210,7 @@ export default function buildLaunchBarConfig() {
id: "_lbMobileTabSwitcher",
title: t("hidden-subtree.tab-switcher-title"),
type: "launcher",
builtinWidget: "mobileTabSwitcher",
icon: "bx bx-rectangle"
builtinWidget: "mobileTabSwitcher"
}
];

View File

@@ -41,6 +41,6 @@ Do note that the theme will be based off of the legacy theme. To override that a
## Step 5. Making changes
Simply go back to the note and change according to needs. To apply the changes to the current window, press <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>R </kbd> to refresh.
Simply go back to the note and change according to needs. To apply the changes to the current window, press <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>R</kbd> to refresh.
It's a good idea to keep two windows, one for editing and the other one for previewing the changes.

View File

@@ -1,6 +1,6 @@
# Creating an icon pack
> [!NOTE]
> e This page describes how to create custom icon packs. For a general description of how to use already existing icon packs, see <a class="reference-link" href="../Basic%20Concepts%20and%20Features/Themes/Icon%20Packs.md">Icon Packs</a>.
> This page describes how to create custom icon packs. For a general description of how to use already existing icon packs, see <a class="reference-link" href="../Basic%20Concepts%20and%20Features/Themes/Icon%20Packs.md">Icon Packs</a>.
## Supported formats
@@ -49,9 +49,9 @@ The icon pack manifest is a JSON file with the following structure:
"bx-ball": {
"glyph": "\ue9c2",
"terms": [ "ball" ]
},
},
"bxs-party": {
"glyph": "\uec92"
"glyph": "\uec92",
"terms": [ "party" ]
}
}

View File

@@ -12,10 +12,10 @@ The `appThemeBase` label can be set to one of the following values:
## Overrides
Do note that the TriliumNext theme has a few more overrides than the legacy theme, so you might need to suffix `!important` if the style changes are not applied.
Do note that the TriliumNext theme has a few more overrides than the legacy theme. Due to that, it is recommended to use `#trilium-app` with a next theme instead of the `:root` of a legacy theme.
```css
:root {
--launcher-pane-background-color: #0d6efd !important;
#trilium-app {
--launcher-pane-background-color: #0d6efd;
}
```