From fa8287269f48234f9eabc82c6334fa3dbaae182d Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 17:34:04 +0200 Subject: [PATCH 01/16] feat(breadcrumb_badges): integrate note properties tab --- .../src/translations/en/translation.json | 4 +++- apps/client/src/widgets/BreadcrumbBadges.css | 1 + apps/client/src/widgets/BreadcrumbBadges.tsx | 18 +++++++++++++++++- .../src/widgets/ribbon/RibbonDefinition.ts | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 9880e99b5..d92e21d26 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -2146,6 +2146,8 @@ "backlinks_one": "{{count}} backlink", "backlinks_other": "{{count}} backlinks", "backlinks_description_one": "This note is linked from {{count}} other note.\n\nClick to view the list of backlinks.", - "backlinks_description_other": "This note is linked from {{count}} other notes.\n\nClick to view the list of backlinks." + "backlinks_description_other": "This note is linked from {{count}} other notes.\n\nClick to view the list of backlinks.", + "clipped_note": "Web clip", + "clipped_note_description": "This note was originally taken from {{url}}.\n\nClick to navigate to the source webpage." } } diff --git a/apps/client/src/widgets/BreadcrumbBadges.css b/apps/client/src/widgets/BreadcrumbBadges.css index a8fcd6657..ae8b4cad1 100644 --- a/apps/client/src/widgets/BreadcrumbBadges.css +++ b/apps/client/src/widgets/BreadcrumbBadges.css @@ -33,6 +33,7 @@ &.temporarily-editable-badge { --color: #4fa52b; } &.read-only-badge { --color: #e33f3b; } &.share-badge { --color: #3b82f6; } + &.clipped-note-badge { --color: #57a2a5; } &.backlinks-badge { color: var(--badge-text-color); } a { diff --git a/apps/client/src/widgets/BreadcrumbBadges.tsx b/apps/client/src/widgets/BreadcrumbBadges.tsx index f1c6dd60e..42aaea065 100644 --- a/apps/client/src/widgets/BreadcrumbBadges.tsx +++ b/apps/client/src/widgets/BreadcrumbBadges.tsx @@ -8,7 +8,7 @@ import { t } from "../services/i18n"; import { formatDateTime } from "../utils/formatters"; import { BacklinksList, useBacklinkCount } from "./FloatingButtonsDefinitions"; import Dropdown, { DropdownProps } from "./react/Dropdown"; -import { useIsNoteReadOnly, useNoteContext, useStaticTooltip } from "./react/hooks"; +import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useStaticTooltip } from "./react/hooks"; import Icon from "./react/Icon"; import { NoteSizeWidget, useNoteMetadata } from "./ribbon/NoteInfoTab"; import { useShareInfo } from "./shared_info"; @@ -20,6 +20,7 @@ export default function BreadcrumbBadges() { + ); } @@ -114,6 +115,21 @@ function BacklinksBadge() { ); } +function ClippedNoteBadge() { + const { note } = useNoteContext(); + const [ pageUrl ] = useNoteLabel(note, "pageUrl"); + + return (pageUrl && + + ); +} + interface BadgeProps { text?: string; icon?: string; diff --git a/apps/client/src/widgets/ribbon/RibbonDefinition.ts b/apps/client/src/widgets/ribbon/RibbonDefinition.ts index e2779e287..284547639 100644 --- a/apps/client/src/widgets/ribbon/RibbonDefinition.ts +++ b/apps/client/src/widgets/ribbon/RibbonDefinition.ts @@ -67,7 +67,7 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [ title: t("note_properties.info"), icon: "bx bx-info-square", content: NotePropertiesTab, - show: ({ note }) => !!note?.getLabelValue("pageUrl"), + show: ({ note }) => !isNewLayout && !!note?.getLabelValue("pageUrl"), activate: true }, { From a9b453c27a636a6ad794a036587c3d03ae98f454 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 17:43:00 +0200 Subject: [PATCH 02/16] feat(breadcrumb_badges): integrate query/script tab --- .../src/translations/en/translation.json | 6 ++++- apps/client/src/widgets/BreadcrumbBadges.css | 1 + apps/client/src/widgets/BreadcrumbBadges.tsx | 22 ++++++++++++++++++- .../src/widgets/ribbon/RibbonDefinition.ts | 2 +- packages/commons/src/lib/attribute_names.ts | 1 + 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index d92e21d26..1159a8bf8 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -2148,6 +2148,10 @@ "backlinks_description_one": "This note is linked from {{count}} other note.\n\nClick to view the list of backlinks.", "backlinks_description_other": "This note is linked from {{count}} other notes.\n\nClick to view the list of backlinks.", "clipped_note": "Web clip", - "clipped_note_description": "This note was originally taken from {{url}}.\n\nClick to navigate to the source webpage." + "clipped_note_description": "This note was originally taken from {{url}}.\n\nClick to navigate to the source webpage.", + "execute_script": "Run script", + "execute_script_description": "This note is a script note. Click to execute the script.", + "execute_sql": "Run SQL", + "execute_sql_description": "This note is a SQL note. Click to execute the SQL query." } } diff --git a/apps/client/src/widgets/BreadcrumbBadges.css b/apps/client/src/widgets/BreadcrumbBadges.css index ae8b4cad1..758678b01 100644 --- a/apps/client/src/widgets/BreadcrumbBadges.css +++ b/apps/client/src/widgets/BreadcrumbBadges.css @@ -35,6 +35,7 @@ &.share-badge { --color: #3b82f6; } &.clipped-note-badge { --color: #57a2a5; } &.backlinks-badge { color: var(--badge-text-color); } + &.execute-badge { --color: #f59e0b; } a { color: inherit !important; diff --git a/apps/client/src/widgets/BreadcrumbBadges.tsx b/apps/client/src/widgets/BreadcrumbBadges.tsx index 42aaea065..a7ccbae9f 100644 --- a/apps/client/src/widgets/BreadcrumbBadges.tsx +++ b/apps/client/src/widgets/BreadcrumbBadges.tsx @@ -8,7 +8,7 @@ import { t } from "../services/i18n"; import { formatDateTime } from "../utils/formatters"; import { BacklinksList, useBacklinkCount } from "./FloatingButtonsDefinitions"; import Dropdown, { DropdownProps } from "./react/Dropdown"; -import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useStaticTooltip } from "./react/hooks"; +import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean, useStaticTooltip } from "./react/hooks"; import Icon from "./react/Icon"; import { NoteSizeWidget, useNoteMetadata } from "./ribbon/NoteInfoTab"; import { useShareInfo } from "./shared_info"; @@ -21,6 +21,7 @@ export default function BreadcrumbBadges() { + ); } @@ -130,6 +131,25 @@ function ClippedNoteBadge() { ); } +function ExecuteBadge() { + const { note, parentComponent } = useNoteContext(); + const isScript = note?.isTriliumScript(); + const isSql = note?.isTriliumSqlite(); + const isExecutable = isScript || isSql; + const [ executeDescription ] = useNoteLabel(note, "executeDescription"); + const [ executeButton ] = useNoteLabelBoolean(note, "executeButton"); + + return (note && isExecutable && (executeDescription || executeButton) && + parentComponent.triggerCommand("runActiveNote")} + /> + ); +} + interface BadgeProps { text?: string; icon?: string; diff --git a/apps/client/src/widgets/ribbon/RibbonDefinition.ts b/apps/client/src/widgets/ribbon/RibbonDefinition.ts index 284547639..766f07df7 100644 --- a/apps/client/src/widgets/ribbon/RibbonDefinition.ts +++ b/apps/client/src/widgets/ribbon/RibbonDefinition.ts @@ -38,7 +38,7 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [ icon: "bx bx-play", content: ScriptTab, activate: true, - show: ({ note }) => note && + show: ({ note }) => note && !isNewLayout && (note.isTriliumScript() || note.isTriliumSqlite()) && (note.hasLabel("executeDescription") || note.hasLabel("executeButton")) }, diff --git a/packages/commons/src/lib/attribute_names.ts b/packages/commons/src/lib/attribute_names.ts index 0295af0b2..ebbe22123 100644 --- a/packages/commons/src/lib/attribute_names.ts +++ b/packages/commons/src/lib/attribute_names.ts @@ -5,6 +5,7 @@ type Labels = { color: string; iconClass: string; workspaceIconClass: string; + executeButton: boolean; executeDescription: string; executeTitle: string; limit: string; // should be probably be number From 0856d3dbdfd89fb04a995ea185b3ac89980c035b Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 18:00:49 +0200 Subject: [PATCH 03/16] fix(layout): note title padding on full-height note --- apps/client/src/widgets/note_title.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/note_title.css b/apps/client/src/widgets/note_title.css index 8769c74ae..174c7d786 100644 --- a/apps/client/src/widgets/note_title.css +++ b/apps/client/src/widgets/note_title.css @@ -75,7 +75,8 @@ body.experimental-feature-new-layout { } } - .scrolling-container:has(> :is(.note-detail.full-height, .note-list-widget.full-height)) { + .scrolling-container:has(> :is(.note-detail.full-height, .note-list-widget.full-height)), + .note-split.type-book { .title-row, .title-details { width: 100%; @@ -84,7 +85,7 @@ body.experimental-feature-new-layout { } .title-row { - margin-top: 0; + padding: 0; } .title-details { From 0eed72b88843b3a7aad1c64d69cd52ebb56ffb50 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 18:53:48 +0200 Subject: [PATCH 04/16] feat(note_bars): view type switcher --- apps/client/src/widgets/NoteTitleDetails.tsx | 45 ++------------- .../note_bars/CollectionProperties.tsx | 29 ++++++++++ apps/client/src/widgets/note_title.css | 3 +- .../ribbon/CollectionPropertiesTab.tsx | 55 +++++++++++-------- 4 files changed, 67 insertions(+), 65 deletions(-) create mode 100644 apps/client/src/widgets/note_bars/CollectionProperties.tsx diff --git a/apps/client/src/widgets/NoteTitleDetails.tsx b/apps/client/src/widgets/NoteTitleDetails.tsx index c9992578f..60618ecb5 100644 --- a/apps/client/src/widgets/NoteTitleDetails.tsx +++ b/apps/client/src/widgets/NoteTitleDetails.tsx @@ -1,46 +1,13 @@ -import { type ComponentChild } from "preact"; - -import { formatDateTime } from "../utils/formatters"; -import { useNoteContext, useStaticTooltip } from "./react/hooks"; -import { joinElements } from "./react/react_utils"; -import { useNoteMetadata } from "./ribbon/NoteInfoTab"; -import { Trans } from "react-i18next"; -import { useRef } from "preact/hooks"; +import CollectionProperties from "./note_bars/CollectionProperties"; +import { useNoteContext, useNoteProperty } from "./react/hooks"; export default function NoteTitleDetails() { - const { note, noteContext } = useNoteContext(); - const isHiddenNote = note?.noteId.startsWith("_"); - const isDefaultView = noteContext?.viewScope?.viewMode === "default"; + const { note } = useNoteContext(); + const noteType = useNoteProperty(note, "type"); - const items: ComponentChild[] = [].filter(item => !!item); - - return items.length && ( + return (
- {joinElements(items, " • ")} + {note && noteType === "book" && }
); } - -function TextWithValue({ i18nKey, value, valueTooltip }: { - i18nKey: string; - value: string; - valueTooltip: string; -}) { - const listItemRef = useRef(null); - useStaticTooltip(listItemRef, { - selector: "span.value", - title: valueTooltip, - popperConfig: { placement: "bottom" } - }); - - return ( -
  • - {value} as React.ReactElement - }} - /> -
  • - ); -} diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx new file mode 100644 index 000000000..8436f3af6 --- /dev/null +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -0,0 +1,29 @@ +import FNote from "../../entities/fnote"; +import { ViewTypeOptions } from "../collections/interface"; +import Dropdown from "../react/Dropdown"; +import { FormListItem } from "../react/FormList"; +import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab"; + +export default function CollectionProperties({ note }: { note: FNote }) { + return ( + + ); +} + +function ViewTypeSwitcher({ note }: { note: FNote }) { + const [ viewType, setViewType ] = useViewType(note); + + return ( + + {Object.entries(VIEW_TYPE_MAPPINGS).map(([ key, label ]) => ( + setViewType(key)} + checked={viewType === key} + >{label} + ))} + + ); +} diff --git a/apps/client/src/widgets/note_title.css b/apps/client/src/widgets/note_title.css index 174c7d786..668695cd9 100644 --- a/apps/client/src/widgets/note_title.css +++ b/apps/client/src/widgets/note_title.css @@ -89,8 +89,7 @@ body.experimental-feature-new-layout { } .title-details { - margin-bottom: 0.2em; - opacity: 0.65; + padding-bottom: 0.2em; font-size: 0.8em; } } diff --git a/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx b/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx index 374633c49..7df070879 100644 --- a/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx +++ b/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx @@ -12,34 +12,41 @@ import FormCheckbox from "../react/FormCheckbox"; import FormTextBox from "../react/FormTextBox"; import { ComponentChildren } from "preact"; import { ViewTypeOptions } from "../collections/interface"; -import { FormDropdownDivider, FormListItem } from "../react/FormList"; +import { isExperimentalFeatureEnabled } from "../../services/experimental_features"; -const VIEW_TYPE_MAPPINGS: Record = { - grid: t("book_properties.grid"), - list: t("book_properties.list"), - calendar: t("book_properties.calendar"), - table: t("book_properties.table"), - geoMap: t("book_properties.geo-map"), - board: t("book_properties.board"), - presentation: t("book_properties.presentation") +export const VIEW_TYPE_MAPPINGS: Record = { + grid: t("book_properties.grid"), + list: t("book_properties.list"), + calendar: t("book_properties.calendar"), + table: t("book_properties.table"), + geoMap: t("book_properties.geo-map"), + board: t("book_properties.board"), + presentation: t("book_properties.presentation") }; -export default function CollectionPropertiesTab({ note }: TabContext) { - const [ viewType, setViewType ] = useNoteLabel(note, "viewType"); - const defaultViewType = (note?.type === "search" ? "list" : "grid"); - const viewTypeWithDefault = (viewType ?? defaultViewType) as ViewTypeOptions; - const properties = bookPropertiesConfig[viewTypeWithDefault].properties; +const isNewLayout = isExperimentalFeatureEnabled("new-layout"); - return ( -
    - {note && ( - <> - - - - )} -
    - ); +export default function CollectionPropertiesTab({ note }: TabContext) { + const [viewType, setViewType] = useViewType(note); + const properties = bookPropertiesConfig[viewType].properties; + + return ( +
    + {note && ( + <> + {!isNewLayout && } + + + )} +
    + ); +} + +export function useViewType(note: FNote | null | undefined) { + const [ viewType, setViewType ] = useNoteLabel(note, "viewType"); + const defaultViewType = (note?.type === "search" ? "list" : "grid"); + const viewTypeWithDefault = (viewType ?? defaultViewType) as ViewTypeOptions; + return [ viewTypeWithDefault, setViewType ] as const; } function CollectionTypeSwitcher({ viewType, setViewType }: { viewType: string, setViewType: (newValue: string) => void }) { From b540111fa459a9acc3eabeac8a63833869914d43 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 18:59:28 +0200 Subject: [PATCH 05/16] feat(note_bars): add icons to view type switcher --- .../note_bars/CollectionProperties.tsx | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index 8436f3af6..ac16eb1e5 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -2,8 +2,19 @@ import FNote from "../../entities/fnote"; import { ViewTypeOptions } from "../collections/interface"; import Dropdown from "../react/Dropdown"; import { FormListItem } from "../react/FormList"; +import Icon from "../react/Icon"; import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab"; +const ICON_MAPPINGS: Record = { + grid: "bx bxs-grid", + list: "bx bx-list-ul", + calendar: "bx bx-calendar", + table: "bx bx-table", + geoMap: "bx bx-map-alt", + board: "bx bx-columns", + presentation: "bx bx-rectangle" +}; + export default function CollectionProperties({ note }: { note: FNote }) { return ( @@ -15,13 +26,18 @@ function ViewTypeSwitcher({ note }: { note: FNote }) { return ( +   + {VIEW_TYPE_MAPPINGS[viewType]} + } > {Object.entries(VIEW_TYPE_MAPPINGS).map(([ key, label ]) => ( setViewType(key)} - checked={viewType === key} + selected={viewType === key} + disabled={viewType === key} + icon={ICON_MAPPINGS[key as ViewTypeOptions]} >{label} ))} From fec5ee9335b8b797a12ef6152f7d0d2281b820e0 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 19:17:23 +0200 Subject: [PATCH 06/16] feat(note_bars/collection): integrate show archived notes --- .../note_bars/CollectionProperties.tsx | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index ac16eb1e5..8cbbc540b 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -1,9 +1,12 @@ +import { t } from "i18next"; import FNote from "../../entities/fnote"; import { ViewTypeOptions } from "../collections/interface"; import Dropdown from "../react/Dropdown"; -import { FormListItem } from "../react/FormList"; +import { FormListItem, FormListToggleableItem } from "../react/FormList"; import Icon from "../react/Icon"; import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab"; +import { BookProperty, CheckBoxProperty } from "../ribbon/collection-properties-config"; +import { useNoteLabel, useNoteLabelBoolean } from "../react/hooks"; const ICON_MAPPINGS: Record = { grid: "bx bxs-grid", @@ -17,7 +20,10 @@ const ICON_MAPPINGS: Record = { export default function CollectionProperties({ note }: { note: FNote }) { return ( - + <> + + + ); } @@ -43,3 +49,36 @@ function ViewTypeSwitcher({ note }: { note: FNote }) { ); } + +function ViewOptions({ note }: { note: FNote }) { + return ( + + + + ); +} + +function ViewProperty({ note, property }: { note: FNote, property: BookProperty }) { + switch (property.type) { + case "checkbox": + return ; + } +} + +function CheckBoxPropertyView({ note, property }: { note: FNote, property: CheckBoxProperty }) { + const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel); + return ( + + ); +} From 0de67b6a69a00607ec461fb0510de0fc5593df74 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 19:29:27 +0200 Subject: [PATCH 07/16] feat(note_bars/collection): support button properties --- .../note_bars/CollectionProperties.tsx | 48 +++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index 8cbbc540b..0de1e363a 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -2,11 +2,13 @@ import { t } from "i18next"; import FNote from "../../entities/fnote"; import { ViewTypeOptions } from "../collections/interface"; import Dropdown from "../react/Dropdown"; -import { FormListItem, FormListToggleableItem } from "../react/FormList"; +import { FormDropdownDivider, FormListItem, FormListToggleableItem } from "../react/FormList"; import Icon from "../react/Icon"; import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab"; -import { BookProperty, CheckBoxProperty } from "../ribbon/collection-properties-config"; +import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty } from "../ribbon/collection-properties-config"; import { useNoteLabel, useNoteLabelBoolean } from "../react/hooks"; +import { useContext } from "preact/hooks"; +import { ParentComponent } from "../react/react_utils"; const ICON_MAPPINGS: Record = { grid: "bx bxs-grid", @@ -19,17 +21,17 @@ const ICON_MAPPINGS: Record = { }; export default function CollectionProperties({ note }: { note: FNote }) { + const [ viewType, setViewType ] = useViewType(note); + return ( <> - - + + ); } -function ViewTypeSwitcher({ note }: { note: FNote }) { - const [ viewType, setViewType ] = useViewType(note); - +function ViewTypeSwitcher({ note, viewType, setViewType }: { note: FNote, viewType: ViewTypeOptions, setViewType: (newValue: ViewTypeOptions) => void }) { return ( @@ -40,7 +42,7 @@ function ViewTypeSwitcher({ note }: { note: FNote }) { {Object.entries(VIEW_TYPE_MAPPINGS).map(([ key, label ]) => ( setViewType(key)} + onClick={() => setViewType(key as ViewTypeOptions)} selected={viewType === key} disabled={viewType === key} icon={ICON_MAPPINGS[key as ViewTypeOptions]} @@ -50,7 +52,9 @@ function ViewTypeSwitcher({ note }: { note: FNote }) { ); } -function ViewOptions({ note }: { note: FNote }) { +function ViewOptions({ note, viewType }: { note: FNote, viewType: ViewTypeOptions }) { + const properties = bookPropertiesConfig[viewType].properties; + return ( + + {properties.length > 0 && } + {properties.map(property => ( + + ))} ); } function ViewProperty({ note, property }: { note: FNote, property: BookProperty }) { switch (property.type) { + case "button": + return ; case "checkbox": return ; } } +function ButtonPropertyView({ note, property }: { note: FNote, property: ButtonProperty }) { + const parentComponent = useContext(ParentComponent); + + return ( + { + if (!parentComponent) return; + property.onClick({ + note, + triggerCommand: parentComponent.triggerCommand.bind(parentComponent) + }); + }} + >{property.label} + ); +} + function CheckBoxPropertyView({ note, property }: { note: FNote, property: CheckBoxProperty }) { const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel); return ( @@ -82,3 +111,4 @@ function CheckBoxPropertyView({ note, property }: { note: FNote, property: Check /> ); } + From a1513a35673d6a34a3dc2df21d24b0e6c74d3945 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 19:34:22 +0200 Subject: [PATCH 08/16] feat(note_bars/collection): support split button properties --- .../widgets/note_bars/CollectionProperties.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index 0de1e363a..830ef67c2 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -2,10 +2,10 @@ import { t } from "i18next"; import FNote from "../../entities/fnote"; import { ViewTypeOptions } from "../collections/interface"; import Dropdown from "../react/Dropdown"; -import { FormDropdownDivider, FormListItem, FormListToggleableItem } from "../react/FormList"; +import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList"; import Icon from "../react/Icon"; import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab"; -import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty } from "../ribbon/collection-properties-config"; +import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, SplitButtonProperty } from "../ribbon/collection-properties-config"; import { useNoteLabel, useNoteLabelBoolean } from "../react/hooks"; import { useContext } from "preact/hooks"; import { ParentComponent } from "../react/react_utils"; @@ -78,6 +78,8 @@ function ViewProperty({ note, property }: { note: FNote, property: BookProperty switch (property.type) { case "button": return ; + case "split-button": + return ; case "checkbox": return ; } @@ -101,6 +103,17 @@ function ButtonPropertyView({ note, property }: { note: FNote, property: ButtonP ); } +function SplitButtonPropertyView({ note, property }: { note: FNote, property: SplitButtonProperty }) { + const parentComponent = useContext(ParentComponent); + const ItemsComponent = property.items; + + return (parentComponent && + + + + ); +} + function CheckBoxPropertyView({ note, property }: { note: FNote, property: CheckBoxProperty }) { const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel); return ( From 1a9fb34a6e946ef4acd023c7d2bdba2ba7f1df7f Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 19:37:04 +0200 Subject: [PATCH 09/16] feat(note_bars/collection): support dropdown menu click action --- .../src/widgets/note_bars/CollectionProperties.tsx | 10 +++++++++- apps/client/src/widgets/react/FormList.tsx | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index 830ef67c2..bee74b513 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -106,9 +106,17 @@ function ButtonPropertyView({ note, property }: { note: FNote, property: ButtonP function SplitButtonPropertyView({ note, property }: { note: FNote, property: SplitButtonProperty }) { const parentComponent = useContext(ParentComponent); const ItemsComponent = property.items; + const clickContext = parentComponent && { + note, + triggerCommand: parentComponent.triggerCommand.bind(parentComponent) + }; return (parentComponent && - + clickContext && property.onClick(clickContext)} + > ); diff --git a/apps/client/src/widgets/react/FormList.tsx b/apps/client/src/widgets/react/FormList.tsx index dd5948cb3..42f43a044 100644 --- a/apps/client/src/widgets/react/FormList.tsx +++ b/apps/client/src/widgets/react/FormList.tsx @@ -206,10 +206,11 @@ export function FormDropdownDivider() { return
    ; } -export function FormDropdownSubmenu({ icon, title, children, dropStart }: { +export function FormDropdownSubmenu({ icon, title, children, dropStart, onDropdownToggleClicked }: { icon: string, title: ComponentChildren, children: ComponentChildren, + onDropdownToggleClicked?: () => void, dropStart?: boolean }) { const [ openOnMobile, setOpenOnMobile ] = useState(false); @@ -224,6 +225,10 @@ export function FormDropdownSubmenu({ icon, title, children, dropStart }: { if (isMobile()) { setOpenOnMobile(!openOnMobile); } + + if (onDropdownToggleClicked) { + onDropdownToggleClicked(); + } }} > {" "} From 9f4757af5b0179e049ac49681823f58434af6320 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 19:39:07 +0200 Subject: [PATCH 10/16] chore(note_bars/collection): put archived notes at the end --- .../src/widgets/note_bars/CollectionProperties.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index bee74b513..5f53f5685 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -6,7 +6,7 @@ import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListTogglea import Icon from "../react/Icon"; import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab"; import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, SplitButtonProperty } from "../ribbon/collection-properties-config"; -import { useNoteLabel, useNoteLabelBoolean } from "../react/hooks"; +import { useNoteLabelBoolean } from "../react/hooks"; import { useContext } from "preact/hooks"; import { ParentComponent } from "../react/react_utils"; @@ -60,16 +60,16 @@ function ViewOptions({ note, viewType }: { note: FNote, viewType: ViewTypeOption buttonClassName="bx bx-cog icon-action" hideToggleArrow > + {properties.map(property => ( + + ))} + {properties.length > 0 && } + - - {properties.length > 0 && } - {properties.map(property => ( - - ))} ); } From e766b8241809a4ea1ea0aceea313e15f4799fdda Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 19:44:22 +0200 Subject: [PATCH 11/16] feat(note_bars/collection): add icon to checkboxes --- apps/client/src/widgets/note_bars/CollectionProperties.tsx | 2 ++ .../src/widgets/ribbon/collection-properties-config.tsx | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index 5f53f5685..7042e80d3 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -67,6 +67,7 @@ function ViewOptions({ note, viewType }: { note: FNote, viewType: ViewTypeOption @@ -126,6 +127,7 @@ function CheckBoxPropertyView({ note, property }: { note: FNote, property: Check const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel); return ( + bindToLabel: FilterLabelsByType; + icon?: string; } export interface ButtonProperty { @@ -107,11 +108,13 @@ export const bookPropertiesConfig: Record = { properties: [ { label: t("book_properties_config.hide-weekends"), + icon: "bx bx-calendar-week", type: "checkbox", bindToLabel: "calendar:hideWeekends" }, { label: t("book_properties_config.display-week-numbers"), + icon: "bx bx-hash", type: "checkbox", bindToLabel: "calendar:weekNumbers" } @@ -147,6 +150,7 @@ export const bookPropertiesConfig: Record = { }, { label: t("book_properties_config.show-scale"), + icon: "bx bx-ruler", type: "checkbox", bindToLabel: "map:scale" } From 00df3c3d1fde68c9dcde4a95b89605da0eee2011 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 19:51:40 +0200 Subject: [PATCH 12/16] feat(note_bars/collection): support number fields --- .../note_bars/CollectionProperties.tsx | 30 +++++++++++++++++-- apps/client/src/widgets/note_title.css | 9 ++++++ .../ribbon/CollectionPropertiesTab.tsx | 2 +- .../ribbon/collection-properties-config.tsx | 2 ++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index 7042e80d3..e8b1c82bc 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -5,10 +5,11 @@ import Dropdown from "../react/Dropdown"; import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList"; import Icon from "../react/Icon"; import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab"; -import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, SplitButtonProperty } from "../ribbon/collection-properties-config"; -import { useNoteLabelBoolean } from "../react/hooks"; +import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config"; +import { useNoteLabel, useNoteLabelBoolean } from "../react/hooks"; import { useContext } from "preact/hooks"; import { ParentComponent } from "../react/react_utils"; +import FormTextBox from "../react/FormTextBox"; const ICON_MAPPINGS: Record = { grid: "bx bxs-grid", @@ -83,6 +84,8 @@ function ViewProperty({ note, property }: { note: FNote, property: BookProperty return ; case "checkbox": return ; + case "number": + return ; } } @@ -123,6 +126,29 @@ function SplitButtonPropertyView({ note, property }: { note: FNote, property: Sp ); } +function NumberPropertyView({ note, property }: { note: FNote, property: NumberProperty }) { + //@ts-expect-error Interop with text box which takes in string values even for numbers. + const [ value, setValue ] = useNoteLabel(note, property.bindToLabel); + const disabled = property.disabled?.(note); + + return ( + e.stopPropagation()} + > + {property.label} + + + ); +} + function CheckBoxPropertyView({ note, property }: { note: FNote, property: CheckBoxProperty }) { const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel); return ( diff --git a/apps/client/src/widgets/note_title.css b/apps/client/src/widgets/note_title.css index 668695cd9..0d9912b33 100644 --- a/apps/client/src/widgets/note_title.css +++ b/apps/client/src/widgets/note_title.css @@ -37,6 +37,15 @@ body.experimental-feature-new-layout { padding-inline-start: 24px; } + .title-details { + .dropdown-menu { + input.form-control { + padding: 2px 8px; + margin-left: 1em; + } + } + } + .title-row { margin-left: 12px; diff --git a/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx b/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx index 7df070879..4af8247a3 100644 --- a/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx +++ b/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx @@ -155,7 +155,7 @@ function NumberPropertyView({ note, property }: { note: FNote, property: NumberP diff --git a/apps/client/src/widgets/ribbon/collection-properties-config.tsx b/apps/client/src/widgets/ribbon/collection-properties-config.tsx index e8db421d3..9dddae75a 100644 --- a/apps/client/src/widgets/ribbon/collection-properties-config.tsx +++ b/apps/client/src/widgets/ribbon/collection-properties-config.tsx @@ -41,6 +41,7 @@ export interface NumberProperty { bindToLabel: FilterLabelsByType; width?: number; min?: number; + icon?: string; disabled?: (note: FNote) => boolean; } @@ -160,6 +161,7 @@ export const bookPropertiesConfig: Record = { properties: [ { label: t("book_properties_config.max-nesting-depth"), + icon: "bx bx-subdirectory-right", type: "number", bindToLabel: "maxNestingDepth", width: 65, From 53b7d93efbc4dab83a5647af47136a53eaf8561c Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 20:09:25 +0200 Subject: [PATCH 13/16] feat(note_bars/collection): support comboboxes --- .../src/translations/en/translation.json | 2 +- .../note_bars/CollectionProperties.tsx | 44 ++++++++++++++++++- .../ribbon/collection-properties-config.tsx | 5 ++- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 1159a8bf8..2e63ef777 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -2031,7 +2031,7 @@ "book_properties_config": { "hide-weekends": "Hide weekends", "display-week-numbers": "Display week numbers", - "map-style": "Map style:", + "map-style": "Map style", "max-nesting-depth": "Max nesting depth:", "raster": "Raster", "vector_light": "Vector (Light)", diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index e8b1c82bc..142ed6ca6 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -5,11 +5,12 @@ import Dropdown from "../react/Dropdown"; import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList"; import Icon from "../react/Icon"; import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab"; -import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config"; -import { useNoteLabel, useNoteLabelBoolean } from "../react/hooks"; +import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxItem, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config"; +import { useNoteLabel, useNoteLabelBoolean, useNoteLabelWithDefault } from "../react/hooks"; import { useContext } from "preact/hooks"; import { ParentComponent } from "../react/react_utils"; import FormTextBox from "../react/FormTextBox"; +import { Fragment } from "preact/jsx-runtime"; const ICON_MAPPINGS: Record = { grid: "bx bxs-grid", @@ -86,6 +87,8 @@ function ViewProperty({ note, property }: { note: FNote, property: BookProperty return ; case "number": return ; + case "combobox": + return ; } } @@ -149,6 +152,43 @@ function NumberPropertyView({ note, property }: { note: FNote, property: NumberP ); } +function ComboBoxPropertyView({ note, property }: { note: FNote, property: ComboBoxProperty }) { + const [ value, setValue ] = useNoteLabelWithDefault(note, property.bindToLabel, property.defaultValue ?? ""); + + function renderItem(option: ComboBoxItem) { + return ( + setValue(option.value)} + > + {option.label} + + ); + } + + return ( + + {(property.options).map((option, index) => { + if ("items" in option) { + return ( + + {option.title} + {option.items.map(renderItem)} + {index < property.options.length - 1 && } + + ); + } else { + return renderItem(option); + } + })} + + ) +} + function CheckBoxPropertyView({ note, property }: { note: FNote, property: CheckBoxProperty }) { const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel); return ( diff --git a/apps/client/src/widgets/ribbon/collection-properties-config.tsx b/apps/client/src/widgets/ribbon/collection-properties-config.tsx index 9dddae75a..1f79217e9 100644 --- a/apps/client/src/widgets/ribbon/collection-properties-config.tsx +++ b/apps/client/src/widgets/ribbon/collection-properties-config.tsx @@ -45,7 +45,7 @@ export interface NumberProperty { disabled?: (note: FNote) => boolean; } -interface ComboBoxItem { +export interface ComboBoxItem { value: string; label: string; } @@ -58,6 +58,7 @@ interface ComboBoxGroup { export interface ComboBoxProperty { type: "combobox", label: string; + icon?: string; bindToLabel: FilterLabelsByType; /** * The default value is used when the label is not set. @@ -125,6 +126,7 @@ export const bookPropertiesConfig: Record = { properties: [ { label: t("book_properties_config.map-style"), + icon: "bx bx-palette", type: "combobox", bindToLabel: "map:style", defaultValue: DEFAULT_MAP_LAYER_NAME, @@ -177,6 +179,7 @@ export const bookPropertiesConfig: Record = { { label: "Theme", type: "combobox", + icon: "bx bx-palette", bindToLabel: "presentation:theme", defaultValue: DEFAULT_THEME, options: getPresentationThemes().map(theme => ({ From 8ab9e304044beddf7aba0243bd45e99f2dde2d51 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 20:13:04 +0200 Subject: [PATCH 14/16] chore(note_bars/collection): disable ribbon tab --- .../widgets/note_bars/CollectionProperties.tsx | 15 ++++++++------- .../client/src/widgets/ribbon/RibbonDefinition.ts | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index 142ed6ca6..ab74b4324 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -1,16 +1,17 @@ import { t } from "i18next"; +import { useContext } from "preact/hooks"; +import { Fragment } from "preact/jsx-runtime"; + import FNote from "../../entities/fnote"; import { ViewTypeOptions } from "../collections/interface"; import Dropdown from "../react/Dropdown"; import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList"; -import Icon from "../react/Icon"; -import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab"; -import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxItem, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config"; -import { useNoteLabel, useNoteLabelBoolean, useNoteLabelWithDefault } from "../react/hooks"; -import { useContext } from "preact/hooks"; -import { ParentComponent } from "../react/react_utils"; import FormTextBox from "../react/FormTextBox"; -import { Fragment } from "preact/jsx-runtime"; +import { useNoteLabel, useNoteLabelBoolean, useNoteLabelWithDefault } from "../react/hooks"; +import Icon from "../react/Icon"; +import { ParentComponent } from "../react/react_utils"; +import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxItem, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config"; +import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab"; const ICON_MAPPINGS: Record = { grid: "bx bxs-grid", diff --git a/apps/client/src/widgets/ribbon/RibbonDefinition.ts b/apps/client/src/widgets/ribbon/RibbonDefinition.ts index 766f07df7..d29cd181f 100644 --- a/apps/client/src/widgets/ribbon/RibbonDefinition.ts +++ b/apps/client/src/widgets/ribbon/RibbonDefinition.ts @@ -60,7 +60,7 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [ title: t("book_properties.book_properties"), icon: "bx bx-book", content: CollectionPropertiesTab, - show: ({ note }) => note?.type === "book" || note?.type === "search", + show: ({ note }) => !isNewLayout && note?.type === "book" || note?.type === "search", toggleCommand: "toggleRibbonTabBookProperties" }, { From c76ff2d371dc0ca93ecb71eae153be94492ce19a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 20:19:06 +0200 Subject: [PATCH 15/16] feat(note_bars/collection): add a help button --- .../widgets/FloatingButtonsDefinitions.tsx | 3 ++- .../note_bars/CollectionProperties.tsx | 22 ++++++++++++++++--- apps/client/src/widgets/note_title.css | 6 +++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/apps/client/src/widgets/FloatingButtonsDefinitions.tsx b/apps/client/src/widgets/FloatingButtonsDefinitions.tsx index 1c7a904a9..171ea2fa8 100644 --- a/apps/client/src/widgets/FloatingButtonsDefinitions.tsx +++ b/apps/client/src/widgets/FloatingButtonsDefinitions.tsx @@ -300,8 +300,9 @@ function ExportImageButtons({ note, triggerEvent, isDefaultViewMode }: FloatingB function InAppHelpButton({ note }: FloatingButtonContext) { const helpUrl = getHelpUrlForNote(note); + const isEnabled = !!helpUrl && (!isNewLayout || (note?.type !== "book")); - return !!helpUrl && ( + return isEnabled && ( = { grid: "bx bxs-grid", @@ -28,13 +31,15 @@ export default function CollectionProperties({ note }: { note: FNote }) { return ( <> - + +
    + ); } -function ViewTypeSwitcher({ note, viewType, setViewType }: { note: FNote, viewType: ViewTypeOptions, setViewType: (newValue: ViewTypeOptions) => void }) { +function ViewTypeSwitcher({ viewType, setViewType }: { viewType: ViewTypeOptions, setViewType: (newValue: ViewTypeOptions) => void }) { return ( @@ -187,7 +192,7 @@ function ComboBoxPropertyView({ note, property }: { note: FNote, property: Combo } })} - ) + ); } function CheckBoxPropertyView({ note, property }: { note: FNote, property: CheckBoxProperty }) { @@ -202,3 +207,14 @@ function CheckBoxPropertyView({ note, property }: { note: FNote, property: Check ); } +function HelpButton({ note }: { note: FNote }) { + const helpUrl = getHelpUrlForNote(note); + + return (helpUrl && ( + openInAppHelpFromUrl(helpUrl))} + text={t("help-button.title")} + /> + )); +} diff --git a/apps/client/src/widgets/note_title.css b/apps/client/src/widgets/note_title.css index 0d9912b33..79734e928 100644 --- a/apps/client/src/widgets/note_title.css +++ b/apps/client/src/widgets/note_title.css @@ -38,12 +38,18 @@ body.experimental-feature-new-layout { } .title-details { + padding-inline-end: 16px; + .dropdown-menu { input.form-control { padding: 2px 8px; margin-left: 1em; } } + + .spacer { + flex-grow: 1; + } } .title-row { From e9ac69b8e5892d52ba1d8d0f439355e96f166af6 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 11 Dec 2025 20:33:52 +0200 Subject: [PATCH 16/16] chore(note_bars/collection): address change request --- apps/client/src/widgets/note_bars/CollectionProperties.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index 17e424224..bf05a131e 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -69,7 +69,7 @@ function ViewOptions({ note, viewType }: { note: FNote, viewType: ViewTypeOption hideToggleArrow > {properties.map(property => ( - + ))} {properties.length > 0 && }