feat(status_bar): code mime switcher

This commit is contained in:
Elian Doran
2025-12-13 01:03:57 +02:00
parent 5bad043ed5
commit 23062470f5
3 changed files with 66 additions and 14 deletions

View File

@@ -2163,6 +2163,7 @@
"attributes_one": "{{count}} attribute",
"attributes_other": "{{count}} attributes",
"attributes_title": "Click to open a dedicated pane to edit this note's owned attributes, as well as to see the list of inherited attributes.",
"note_paths_title": "Click to see the paths where this note is placed into the tree."
"note_paths_title": "Click to see the paths where this note is placed into the tree.",
"code_note_switcher": "Change language mode"
}
}

View File

@@ -17,10 +17,10 @@ import { formatDateTime } from "../../utils/formatters";
import { BacklinksList, useBacklinkCount } from "../FloatingButtonsDefinitions";
import Dropdown, { DropdownProps } from "../react/Dropdown";
import { FormDropdownDivider, FormListItem } from "../react/FormList";
import { useActiveNoteContext, useLegacyImperativeHandlers, useStaticTooltip, useTriliumEvent, useTriliumEvents } from "../react/hooks";
import { useActiveNoteContext, useLegacyImperativeHandlers, useNoteProperty, useStaticTooltip, useTriliumEvent, useTriliumEvents } from "../react/hooks";
import Icon from "../react/Icon";
import { ParentComponent } from "../react/react_utils";
import { ContentLanguagesModal, useLanguageSwitcher } from "../ribbon/BasicPropertiesTab";
import { ContentLanguagesModal, NoteTypeCodeNoteList, useLanguageSwitcher, useMimeTypes } from "../ribbon/BasicPropertiesTab";
import AttributeEditor, { AttributeEditorImperativeHandlers } from "../ribbon/components/AttributeEditor";
import InheritedAttributesTab from "../ribbon/InheritedAttributesTab";
import { NoteSizeWidget, useNoteMetadata } from "../ribbon/NoteInfoTab";
@@ -28,6 +28,7 @@ import { NotePathsWidget, useSortedNotePaths } from "../ribbon/NotePathsTab";
import { useAttachments } from "../type_widgets/Attachment";
import { useProcessedLocales } from "../type_widgets/options/components/LocaleSelector";
import Breadcrumb from "./Breadcrumb";
import server from "../../services/server";
interface StatusBarContext {
note: FNote;
@@ -53,6 +54,7 @@ export default function StatusBar() {
<Breadcrumb {...context} />
<div className="actions-row">
<CodeNoteSwitcher {...context} />
<LanguageSwitcher {...context} />
{!isHiddenNote && <NotePaths {...context} />}
<AttributesButton {...attributesContext} />
@@ -75,15 +77,19 @@ function StatusBarDropdown({ children, icon, text, buttonClassName, titleOptions
buttonClassName={clsx("status-bar-dropdown-button", buttonClassName)}
titlePosition="top"
titleOptions={{
...titleOptions,
popperConfig: {
...titleOptions?.popperConfig,
strategy: "fixed"
}
},
...titleOptions
}}
dropdownOptions={{
...dropdownOptions,
autoClose: "outside"
autoClose: "outside",
popperConfig: {
strategy: "fixed",
placement: "top"
},
...dropdownOptions
}}
text={<>
{icon && (<><Icon icon={icon} />&nbsp;</>)}
@@ -340,3 +346,28 @@ function NotePaths({ note, hoistedNoteId, notePath }: StatusBarContext) {
);
}
//#endregion
//#region Code note switcher
function CodeNoteSwitcher({ note }: StatusBarContext) {
const currentNoteMime = useNoteProperty(note, "mime");
const mimeTypes = useMimeTypes();
const correspondingMimeType = useMemo(() => (
mimeTypes.find(m => m.mime === currentNoteMime)
), [ mimeTypes, currentNoteMime ]);
return (
<StatusBarDropdown
icon="bx bx-code-curly"
text={correspondingMimeType?.title}
title={t("status_bar.code_note_switcher")}
dropdownOptions={{ autoClose: true }}
>
<NoteTypeCodeNoteList
mimeTypes={mimeTypes}
changeNoteType={(type, mime) => server.put(`notes/${note.noteId}/type`, { type, mime })}
setModalShown={() => {}}
/>
</StatusBarDropdown>
);
}
//#endregion

View File

@@ -1,4 +1,4 @@
import { NoteType, ToggleInParentResponse } from "@triliumnext/commons";
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";
@@ -66,12 +66,8 @@ function NoteTypeWidget({ note }: { note?: FNote | null }) {
}
export function NoteTypeDropdownContent({ currentNoteType, currentNoteMime, note, setModalShown }: { currentNoteType?: NoteType, currentNoteMime?: string | null, note?: FNote | null, setModalShown: Dispatch<StateUpdater<boolean>> }) {
const [ codeNotesMimeTypes ] = useTriliumOption("codeNotesMimeTypes");
const mimeTypes = useMimeTypes();
const noteTypes = useMemo(() => NOTE_TYPES.filter((nt) => !nt.reserved && !nt.static), []);
const mimeTypes = useMemo(() => {
mime_types.loadMimeTypes();
return mime_types.getMimeTypes().filter(mimeType => mimeType.enabled);
}, [ codeNotesMimeTypes ]);
const changeNoteType = useCallback(async (type: NoteType, mime?: string) => {
if (!note || (type === currentNoteType && mime === currentNoteMime)) {
return;
@@ -130,8 +126,23 @@ export function NoteTypeDropdownContent({ currentNoteType, currentNoteMime, note
}
})}
<NoteTypeCodeNoteList mimeTypes={mimeTypes} changeNoteType={changeNoteType} setModalShown={setModalShown} />
</>
);
}
export function NoteTypeCodeNoteList({ mimeTypes, changeNoteType, setModalShown }: {
mimeTypes: MimeType[];
changeNoteType(type: NoteType, mime: string): void;
setModalShown(shown: boolean): void;
}) {
return (
<>
{mimeTypes.map(({ title, mime }) => (
<FormListItem onClick={() => changeNoteType("code", mime)}>
<FormListItem
key={mime}
onClick={() => changeNoteType("code", mime)}
>
{title}
</FormListItem>
))}
@@ -142,6 +153,15 @@ export function NoteTypeDropdownContent({ currentNoteType, currentNoteMime, note
);
}
export function useMimeTypes() {
const [ codeNotesMimeTypes ] = useTriliumOption("codeNotesMimeTypes");
const mimeTypes = useMemo(() => {
mime_types.loadMimeTypes();
return mime_types.getMimeTypes().filter(mimeType => mimeType.enabled);
}, [ codeNotesMimeTypes ]); // eslint-disable-line react-hooks/exhaustive-deps
return mimeTypes;
}
function NoteTypeOptionsModal({ modalShown, setModalShown }: { modalShown: boolean, setModalShown: (shown: boolean) => void }) {
return (
<Modal