diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index ebd0f2662..17b1b90ae 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -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"
}
}
diff --git a/apps/client/src/widgets/layout/StatusBar.tsx b/apps/client/src/widgets/layout/StatusBar.tsx
index 5d08bb5f6..50db10908 100644
--- a/apps/client/src/widgets/layout/StatusBar.tsx
+++ b/apps/client/src/widgets/layout/StatusBar.tsx
@@ -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() {
+
{!isHiddenNote &&
}
@@ -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 && (<>
>)}
@@ -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 (
+
+ server.put(`notes/${note.noteId}/type`, { type, mime })}
+ setModalShown={() => {}}
+ />
+
+ );
+}
+//#endregion
diff --git a/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx b/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx
index 761577949..75887c0b4 100644
--- a/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx
+++ b/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx
@@ -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
> }) {
- 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
}
})}
+
+ >
+ );
+}
+
+export function NoteTypeCodeNoteList({ mimeTypes, changeNoteType, setModalShown }: {
+ mimeTypes: MimeType[];
+ changeNoteType(type: NoteType, mime: string): void;
+ setModalShown(shown: boolean): void;
+}) {
+ return (
+ <>
{mimeTypes.map(({ title, mime }) => (
- changeNoteType("code", mime)}>
+ changeNoteType("code", mime)}
+ >
{title}
))}
@@ -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 (