diff --git a/apps/client/package.json b/apps/client/package.json index 6861392d06..5c90f4d565 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -36,6 +36,7 @@ "autocomplete.js": "0.38.1", "bootstrap": "5.3.8", "boxicons": "2.1.4", + "clsx": "2.1.1", "color": "5.0.3", "dayjs": "1.11.19", "dayjs-plugin-utc": "0.1.2", diff --git a/apps/client/src/menus/context_menu.ts b/apps/client/src/menus/context_menu.ts index 9efdac65f8..4e28d77680 100644 --- a/apps/client/src/menus/context_menu.ts +++ b/apps/client/src/menus/context_menu.ts @@ -2,7 +2,7 @@ import { KeyboardActionNames } from "@triliumnext/commons"; import keyboardActionService, { getActionSync } from "../services/keyboard_actions.js"; import note_tooltip from "../services/note_tooltip.js"; import utils from "../services/utils.js"; -import { should } from "vitest"; +import { h, JSX, render } from "preact"; export interface ContextMenuOptions { x: number; @@ -15,6 +15,11 @@ export interface ContextMenuOptions { onHide?: () => void; } +export interface CustomMenuItem { + kind: "custom", + componentFn: () => JSX.Element | null; +} + export interface MenuSeparatorItem { kind: "separator"; } @@ -51,7 +56,7 @@ export interface MenuCommandItem { columns?: number; } -export type MenuItem = MenuCommandItem | MenuSeparatorItem | MenuHeader; +export type MenuItem = MenuCommandItem | CustomMenuItem | MenuSeparatorItem | MenuHeader; export type MenuHandler = (item: MenuCommandItem, e: JQuery.MouseDownEvent) => void; export type ContextMenuEvent = PointerEvent | MouseEvent | JQuery.ContextMenuEvent; @@ -202,126 +207,142 @@ class ContextMenu { $group.append($("
").addClass("dropdown-header").text(item.title)); shouldResetGroup = true; } else { - const $icon = $(""); - - if ("uiIcon" in item || "checked" in item) { - const icon = (item.checked ? "bx bx-check" : item.uiIcon); - if (icon) { - $icon.addClass(icon); - } else { - $icon.append(" "); - } + if ("kind" in item && item.kind === "custom") { + // Custom menu item + $group.append(this.createCustomMenuItem(item)); + } else { + // Standard menu item + $group.append(this.createMenuItem(item)); } - const $link = $("") - .append($icon) - .append("   ") // some space between icon and text - .append(item.title); + // After adding a menu item, if the previous item was a separator or header, + // reset the group so that the next item will be appended directly to the parent. + if (shouldResetGroup) { + $group = $parent; + shouldResetGroup = false; + }; + } + } + } + + private createCustomMenuItem(item: CustomMenuItem) { + const element = document.createElement("li"); + element.classList.add("dropdown-custom-item"); + render(h(item.componentFn, {}), element); + return element; + } + + private createMenuItem(item: MenuCommandItem) { + const $icon = $(""); - if ("badges" in item && item.badges) { - for (let badge of item.badges) { - const badgeElement = $(``).text(badge.title); + if ("uiIcon" in item || "checked" in item) { + const icon = (item.checked ? "bx bx-check" : item.uiIcon); + if (icon) { + $icon.addClass(icon); + } else { + $icon.append(" "); + } + } - if (badge.className) { - badgeElement.addClass(badge.className); - } + const $link = $("") + .append($icon) + .append("   ") // some space between icon and text + .append(item.title); - $link.append(badgeElement); - } + if ("badges" in item && item.badges) { + for (let badge of item.badges) { + const badgeElement = $(``).text(badge.title); + + if (badge.className) { + badgeElement.addClass(badge.className); } - if ("keyboardShortcut" in item && item.keyboardShortcut) { - const shortcuts = getActionSync(item.keyboardShortcut).effectiveShortcuts; - if (shortcuts) { - const allShortcuts: string[] = []; - for (const effectiveShortcut of shortcuts) { - allShortcuts.push(effectiveShortcut.split("+") - .map(key => `${key}`) - .join("+")); - } - - if (allShortcuts.length) { - const container = $("").addClass("keyboard-shortcut"); - container.append($(allShortcuts.join(","))); - $link.append(container); - } - } - } else if ("shortcut" in item && item.shortcut) { - $link.append($("").text(item.shortcut)); + $link.append(badgeElement); + } + } + + if ("keyboardShortcut" in item && item.keyboardShortcut) { + const shortcuts = getActionSync(item.keyboardShortcut).effectiveShortcuts; + if (shortcuts) { + const allShortcuts: string[] = []; + for (const effectiveShortcut of shortcuts) { + allShortcuts.push(effectiveShortcut.split("+") + .map(key => `${key}`) + .join("+")); } - const $item = $("
  • ") - .addClass("dropdown-item") - .append($link) - .on("contextmenu", (e) => false) - // important to use mousedown instead of click since the former does not change focus - // (especially important for focused text for spell check) - .on("mousedown", (e) => { - e.stopPropagation(); - - if (e.which !== 1) { - // only left click triggers menu items - return false; - } - - if (this.isMobile && "items" in item && item.items) { - const $item = $(e.target).closest(".dropdown-item"); - - $item.toggleClass("submenu-open"); - $item.find("ul.dropdown-menu").toggleClass("show"); - return false; - } - - if ("handler" in item && item.handler) { - item.handler(item, e); - } - - this.options?.selectMenuItemHandler(item, e); - - // it's important to stop the propagation especially for sub-menus, otherwise the event - // might be handled again by top-level menu - return false; - }); - - $item.on("mouseup", (e) => { - // Prevent submenu from failing to expand on mobile - if (!this.isMobile || !("items" in item && item.items)) { - e.stopPropagation(); - // Hide the content menu on mouse up to prevent the mouse event from propagating to the elements below. - this.hide(); - return false; - } - }); - - if ("enabled" in item && item.enabled !== undefined && !item.enabled) { - $item.addClass("disabled"); + if (allShortcuts.length) { + const container = $("").addClass("keyboard-shortcut"); + container.append($(allShortcuts.join(","))); + $link.append(container); } + } + } else if ("shortcut" in item && item.shortcut) { + $link.append($("").text(item.shortcut)); + } - if ("items" in item && item.items) { - $item.addClass("dropdown-submenu"); - $link.addClass("dropdown-toggle"); + const $item = $("
  • ") + .addClass("dropdown-item") + .append($link) + .on("contextmenu", (e) => false) + // important to use mousedown instead of click since the former does not change focus + // (especially important for focused text for spell check) + .on("mousedown", (e) => { + e.stopPropagation(); + + if (e.which !== 1) { + // only left click triggers menu items + return false; + } - const $subMenu = $("
      ").addClass("dropdown-menu"); - const hasColumns = !!item.columns && item.columns > 1; - if (!this.isMobile && hasColumns) { - $subMenu.css("column-count", item.columns!); - } + if (this.isMobile && "items" in item && item.items) { + const $item = $(e.target).closest(".dropdown-item"); - this.addItems($subMenu, item.items, hasColumns); + $item.toggleClass("submenu-open"); + $item.find("ul.dropdown-menu").toggleClass("show"); + return false; + } - $item.append($subMenu); + if ("handler" in item && item.handler) { + item.handler(item, e); } - $group.append($item); + this.options?.selectMenuItemHandler(item, e); - // After adding a menu item, if the previous item was a separator or header, - // reset the group so that the next item will be appended directly to the parent. - if (shouldResetGroup) { - $group = $parent; - shouldResetGroup = false; - }; + // it's important to stop the propagation especially for sub-menus, otherwise the event + // might be handled again by top-level menu + return false; + }); + + $item.on("mouseup", (e) => { + // Prevent submenu from failing to expand on mobile + if (!this.isMobile || !("items" in item && item.items)) { + e.stopPropagation(); + // Hide the content menu on mouse up to prevent the mouse event from propagating to the elements below. + this.hide(); + return false; + } + }); + + if ("enabled" in item && item.enabled !== undefined && !item.enabled) { + $item.addClass("disabled"); + } + + if ("items" in item && item.items) { + $item.addClass("dropdown-submenu"); + $link.addClass("dropdown-toggle"); + + const $subMenu = $("
        ").addClass("dropdown-menu"); + const hasColumns = !!item.columns && item.columns > 1; + if (!this.isMobile && hasColumns) { + $subMenu.css("column-count", item.columns!); } + + this.addItems($subMenu, item.items, hasColumns); + + $item.append($subMenu); } + return $item; } async hide() { diff --git a/apps/client/src/menus/custom-items/NoteColorPicker.css b/apps/client/src/menus/custom-items/NoteColorPicker.css new file mode 100644 index 0000000000..f4d6f642fc --- /dev/null +++ b/apps/client/src/menus/custom-items/NoteColorPicker.css @@ -0,0 +1,86 @@ +:root { + --note-color-picker-clear-color-cell-background: var(--primary-button-background-color); + --note-color-picker-clear-color-cell-color: var(--main-background-color); + --note-color-picker-clear-color-cell-selection-outline-color: var(--primary-button-border-color); +} + +.note-color-picker { + display: flex; + gap: 8px; + justify-content: space-between; +} + +.note-color-picker .color-cell { + --color-picker-cell-size: 14px; + + width: var(--color-picker-cell-size); + height: var(--color-picker-cell-size); + border-radius: 4px; + background-color: var(--color); +} + +.note-color-picker .color-cell:not(.selected):hover { + transform: scale(1.2); +} + +.note-color-picker .color-cell.disabled-color-cell { + cursor: not-allowed; +} + +.note-color-picker .color-cell.selected { + outline: 2px solid var(--outline-color, var(--color)); + outline-offset: 2px; +} + +/* + * RESET COLOR CELL + */ + +.note-color-picker .color-cell-reset { + --color: var(--note-color-picker-clear-color-cell-background); + --outline-color: var(--note-color-picker-clear-color-cell-selection-outline-color); + + position: relative; + display: flex; + justify-content: center; + align-items: center; +} + +.note-color-picker .color-cell-reset svg { + width: var(--color-picker-cell-size); + height: var(--color-picker-cell-size); + fill: var(--note-color-picker-clear-color-cell-color); +} + +/* + * CUSTOM COLOR CELL + */ + + .note-color-picker .custom-color-cell::before { + position: absolute; + content: "\ed35"; + display: flex; + top: 0; + left: 0; + right: 0; + bottom: 0; + font-size: calc(var(--color-picker-cell-size) * 1.3); + justify-content: center; + align-items: center; + font-family: boxicons; + font-size: 16px; + color: var(--foreground); +} + +.note-color-picker .custom-color-cell { + position: relative; + display: flex; + justify-content: center; + +} + +.note-color-picker .custom-color-cell.custom-color-cell-empty { + background-image: url(./custom-color.png); + background-size: cover; + --foreground: transparent; +} \ No newline at end of file diff --git a/apps/client/src/menus/custom-items/NoteColorPicker.tsx b/apps/client/src/menus/custom-items/NoteColorPicker.tsx new file mode 100644 index 0000000000..08bc7c84ae --- /dev/null +++ b/apps/client/src/menus/custom-items/NoteColorPicker.tsx @@ -0,0 +1,204 @@ +import "./NoteColorPicker.css"; +import { t } from "../../services/i18n"; +import { useCallback, useEffect, useRef, useState} from "preact/hooks"; +import {ComponentChildren} from "preact"; +import attributes from "../../services/attributes"; +import clsx from "clsx"; +import Color, { ColorInstance } from "color"; +import Debouncer from "../../utils/debouncer"; +import FNote from "../../entities/fnote"; +import froca from "../../services/froca"; + +const COLOR_PALETTE = [ + "#e64d4d", "#e6994d", "#e5e64d", "#99e64d", "#4de64d", "#4de699", + "#4de5e6", "#4d99e6", "#4d4de6", "#994de6", "#e64db3" +]; + +export interface NoteColorPickerProps { + /** The target Note instance or its ID string. */ + note: FNote | string | null; +} + +export default function NoteColorPicker(props: NoteColorPickerProps) { + if (!props.note) return null; + + const [note, setNote] = useState(null); + const [currentColor, setCurrentColor] = useState(null); + const [isCustomColor, setIsCustomColor] = useState(false); + + useEffect(() => { + const retrieveNote = async (noteId: string) => { + const noteInstance = await froca.getNote(noteId, true); + if (noteInstance) { + setNote(noteInstance); + } + } + + if (typeof props.note === "string") { + retrieveNote(props.note); // Get the note from the given ID string + } else { + setNote(props.note); + } + }, []); + + useEffect(() => { + const colorLabel = note?.getLabel("color")?.value ?? null; + if (colorLabel) { + let color = tryParseColor(colorLabel); + if (color) { + setCurrentColor(color.hex().toLowerCase()); + } + } + }, [note]); + + useEffect(() => { + setIsCustomColor(currentColor !== null && COLOR_PALETTE.indexOf(currentColor) === -1); + }, [currentColor]) + + const onColorCellClicked = useCallback((color: string | null) => { + if (note) { + if (color !== null) { + attributes.setLabel(note.noteId, "color", color); + } else { + attributes.removeOwnedLabelByName(note, "color"); + } + + setCurrentColor(color); + } + }, [note, currentColor]); + + return
        + + + + {/* https://pictogrammers.com/library/mdi/icon/close/ */} + + + + + + + {COLOR_PALETTE.map((color) => ( + + ))} + + +
        +} + +interface ColorCellProps { + children?: ComponentChildren, + className?: string, + tooltip?: string, + color: string | null, + isSelected: boolean, + isDisabled?: boolean, + onSelect?: (color: string | null) => void +} + +function ColorCell(props: ColorCellProps) { + return
        props.onSelect?.(props.color)}> + {props.children} +
        ; +} + +function CustomColorCell(props: ColorCellProps) { + const [pickedColor, setPickedColor] = useState(null); + const colorInput = useRef(null); + const colorInputDebouncer = useRef | null>(null); + const callbackRef = useRef(props.onSelect); + const isSafari = useRef(/^((?!chrome|android).)*safari/i.test(navigator.userAgent)); + + useEffect(() => { + colorInputDebouncer.current = new Debouncer(250, (color) => { + callbackRef.current?.(color); + setPickedColor(color); + }); + + return () => { + colorInputDebouncer.current?.destroy(); + } + }, []); + + useEffect(() => { + if (props.isSelected && pickedColor === null) { + setPickedColor(props.color); + } + }, [props.isSelected]) + + useEffect(() => { + callbackRef.current = props.onSelect; + }, [props.onSelect]); + + const onSelect = useCallback(() => { + if (pickedColor !== null) { + callbackRef.current?.(pickedColor); + } + + colorInput.current?.click(); + }, [pickedColor]); + + return
        { + // The color picker dropdown will close on Safari if the parent context menu is + // dismissed, so stop the click propagation to prevent dismissing the menu. + isSafari.current && e.stopPropagation(); + }}> + + + {colorInputDebouncer.current?.updateValue(colorInput.current?.value ?? null)}} + style="width: 0; height: 0; opacity: 0" /> + +
        +} + +function getForegroundColor(backgroundColor: string | null) { + if (backgroundColor === null) return "inherit"; + + const colorHsl = tryParseColor(backgroundColor)?.hsl(); + if (colorHsl) { + let l = colorHsl.lightness(); + return colorHsl.saturationl(0).lightness(l >= 50 ? 0 : 100).hex(); + } else { + return "inherit"; + } +} + +function tryParseColor(colorStr: string): ColorInstance | null { + try { + return new Color(colorStr); + } catch(ex) { + console.error(ex); + } + + return null; +} \ No newline at end of file diff --git a/apps/client/src/menus/custom-items/custom-color.png b/apps/client/src/menus/custom-items/custom-color.png new file mode 100644 index 0000000000..4275c56d2e Binary files /dev/null and b/apps/client/src/menus/custom-items/custom-color.png differ diff --git a/apps/client/src/menus/tree_context_menu.ts b/apps/client/src/menus/tree_context_menu.ts index 7384573d85..19e6f4e169 100644 --- a/apps/client/src/menus/tree_context_menu.ts +++ b/apps/client/src/menus/tree_context_menu.ts @@ -1,3 +1,4 @@ +import NoteColorPicker from "./custom-items/NoteColorPicker.jsx"; import treeService from "../services/tree.js"; import froca from "../services/froca.js"; import clipboard from "../services/clipboard.js"; @@ -241,6 +242,19 @@ export default class TreeContextMenu implements SelectMenuItemEventListener { + if (notOptionsOrHelp && selectedNotes.length === 1) { + return NoteColorPicker({note}); + } else { + return null; + } + } + }, + { kind: "separator" }, { title: t("tree-context-menu.import-into-note"), command: "importIntoNote", uiIcon: "bx bx-import", enabled: notSearch && noSelectedNotes && notOptionsOrHelp }, diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css index 7b95d13dfe..eae9423441 100644 --- a/apps/client/src/stylesheets/style.css +++ b/apps/client/src/stylesheets/style.css @@ -494,6 +494,10 @@ body #context-menu-container .dropdown-item > span { width: 100%; } +.dropdown-menu .note-color-picker { + padding: 4px 12px 8px 12px; +} + .cm-editor { height: 100%; outline: none !important; diff --git a/apps/client/src/stylesheets/theme-next/base.css b/apps/client/src/stylesheets/theme-next/base.css index 5976f1dfbe..ba83af30a1 100644 --- a/apps/client/src/stylesheets/theme-next/base.css +++ b/apps/client/src/stylesheets/theme-next/base.css @@ -347,6 +347,12 @@ li.dropdown-item a.dropdown-item-button:focus-visible { outline: 2px solid var(--input-focus-outline-color) !important; } +:root .dropdown-menu .note-color-picker { + padding: 4px 10px; + --note-color-picker-clear-color-cell-background: var(--main-text-color); + --note-color-picker-clear-color-cell-selection-outline-color: var(--main-text-color); +} + /* * TOASTS */ diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 54025d6909..aa8c5926c9 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -2093,5 +2093,10 @@ }, "collections": { "rendering_error": "Unable to show content due to an error." + }, + "note-color": { + "clear-color": "Clear note color", + "set-color": "Set note color", + "set-custom-color": "Set custom note color" } } diff --git a/apps/client/src/translations/ro/translation.json b/apps/client/src/translations/ro/translation.json index b0e412b35c..5750bb3317 100644 --- a/apps/client/src/translations/ro/translation.json +++ b/apps/client/src/translations/ro/translation.json @@ -2095,5 +2095,10 @@ }, "calendar_view": { "delete_note": "Șterge notița..." + }, + "note-color": { + "clear-color": "Înlăturați culoarea notiței", + "set-color": "Setați culoarea notiței", + "set-custom-color": "Setați culoare personalizată pentru notiță" } } diff --git a/apps/client/src/utils/debouncer.ts b/apps/client/src/utils/debouncer.ts new file mode 100644 index 0000000000..9057d039fe --- /dev/null +++ b/apps/client/src/utils/debouncer.ts @@ -0,0 +1,35 @@ +export type DebouncerCallback = (value: T) => void; + +export default class Debouncer { + + private debounceInterval: number; + private callback: DebouncerCallback; + private lastValue: T | undefined; + private timeoutId: any | null = null; + + constructor(debounceInterval: number, onUpdate: DebouncerCallback) { + this.debounceInterval = debounceInterval; + this.callback = onUpdate; + } + + updateValue(value: T) { + this.lastValue = value; + if (this.timeoutId !== null) { + clearTimeout(this.timeoutId); + } + this.timeoutId = setTimeout(this.reportUpdate.bind(this), this.debounceInterval); + } + + destroy() { + if (this.timeoutId !== null) { + this.reportUpdate(); + clearTimeout(this.timeoutId); + } + } + + private reportUpdate() { + if (this.lastValue !== undefined) { + this.callback(this.lastValue); + } + } +} \ No newline at end of file diff --git a/apps/client/src/widgets/collections/board/context_menu.ts b/apps/client/src/widgets/collections/board/context_menu.ts index 0c818a1112..c834b4c8d4 100644 --- a/apps/client/src/widgets/collections/board/context_menu.ts +++ b/apps/client/src/widgets/collections/board/context_menu.ts @@ -1,4 +1,5 @@ import FNote from "../../../entities/fnote"; +import NoteColorPicker from "../../../menus/custom-items/NoteColorPicker"; import contextMenu, { ContextMenuEvent } from "../../../menus/context_menu"; import link_context_menu from "../../../menus/link_context_menu"; import attributes from "../../../services/attributes"; @@ -74,6 +75,11 @@ export function openNoteContextMenu(api: Api, event: ContextMenuEvent, note: FNo uiIcon: "bx bx-trash", handler: () => branches.deleteNotes([ branchId ], false, false) }, + { kind: "separator" }, + { + kind: "custom", + componentFn: () => NoteColorPicker({note}) + } ], selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, note.noteId), }); diff --git a/apps/client/src/widgets/collections/calendar/context_menu.ts b/apps/client/src/widgets/collections/calendar/context_menu.ts index b15ba376d1..0195d131c9 100644 --- a/apps/client/src/widgets/collections/calendar/context_menu.ts +++ b/apps/client/src/widgets/collections/calendar/context_menu.ts @@ -1,8 +1,10 @@ +import NoteColorPicker from "../../../menus/custom-items/NoteColorPicker"; import FNote from "../../../entities/fnote"; import contextMenu, { ContextMenuEvent } from "../../../menus/context_menu"; import link_context_menu from "../../../menus/link_context_menu"; import branches from "../../../services/branches"; import froca from "../../../services/froca"; +import { note } from "mermaid/dist/rendering-util/rendering-elements/shapes/note.js"; import { t } from "../../../services/i18n"; export function openCalendarContextMenu(e: ContextMenuEvent, noteId: string, parentNote: FNote) { @@ -34,6 +36,11 @@ export function openCalendarContextMenu(e: ContextMenuEvent, noteId: string, par await branches.deleteNotes([ branchIdToDelete ], false, false); } } + }, + { kind: "separator" }, + { + kind: "custom", + componentFn: () => NoteColorPicker({note: noteId}) } ], selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, noteId), diff --git a/apps/client/src/widgets/collections/geomap/context_menu.ts b/apps/client/src/widgets/collections/geomap/context_menu.ts index dd583e266a..f4161fad9b 100644 --- a/apps/client/src/widgets/collections/geomap/context_menu.ts +++ b/apps/client/src/widgets/collections/geomap/context_menu.ts @@ -2,6 +2,7 @@ import type { LatLng, LeafletMouseEvent } from "leaflet"; import appContext, { type CommandMappings } from "../../../components/app_context.js"; import contextMenu, { type MenuItem } from "../../../menus/context_menu.js"; import linkContextMenu from "../../../menus/link_context_menu.js"; +import NoteColorPicker from "../../../menus/custom-items/NoteColorPicker.jsx"; import { t } from "../../../services/i18n.js"; import { createNewNote } from "./api.js"; import { copyTextWithToast } from "../../../services/clipboard_ext.js"; @@ -18,7 +19,12 @@ export default function openContextMenu(noteId: string, e: LeafletMouseEvent, is items = [ ...items, { kind: "separator" }, - { title: t("geo-map-context.remove-from-map"), command: "deleteFromMap", uiIcon: "bx bx-trash" } + { title: t("geo-map-context.remove-from-map"), command: "deleteFromMap", uiIcon: "bx bx-trash" }, + { kind: "separator"}, + { + kind: "custom", + componentFn: () => NoteColorPicker({note: noteId}) + } ]; } diff --git a/apps/client/src/widgets/collections/table/context_menu.ts b/apps/client/src/widgets/collections/table/context_menu.ts index eb0a303ae6..80b1c93c2f 100644 --- a/apps/client/src/widgets/collections/table/context_menu.ts +++ b/apps/client/src/widgets/collections/table/context_menu.ts @@ -7,6 +7,7 @@ import link_context_menu from "../../../menus/link_context_menu.js"; import froca from "../../../services/froca.js"; import branches from "../../../services/branches.js"; import Component from "../../../components/component.js"; +import NoteColorPicker from "../../../menus/custom-items/NoteColorPicker.jsx"; import { RefObject } from "preact"; export function useContextMenu(parentNote: FNote, parentComponent: Component | null | undefined, tabulator: RefObject): Partial { @@ -219,6 +220,11 @@ export function showRowContextMenu(parentComponent: Component, e: MouseEvent, ro title: t("table_context_menu.delete_row"), uiIcon: "bx bx-trash", handler: () => branches.deleteNotes([ rowData.branchId ], false, false) + }, + { kind: "separator"}, + { + kind: "custom", + componentFn: () => NoteColorPicker({note: rowData.noteId}) } ], selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, rowData.noteId), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3d1ba5b85..340a6c6768 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -220,6 +220,9 @@ importers: boxicons: specifier: 2.1.4 version: 2.1.4 + clsx: + specifier: 2.1.1 + version: 2.1.1 color: specifier: 5.0.3 version: 5.0.3 @@ -4749,11 +4752,16 @@ packages: resolution: {integrity: sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==} engines: {node: '>=18.0.0'} + '@smithy/core@3.18.3': + resolution: {integrity: sha512-qqpNskkbHOSfrbFbjhYj5o8VMXO26fvN1K/+HbCzUNlTuxgNcPRouUDNm+7D6CkN244WG7aK533Ne18UtJEgAA==} + engines: {node: '>=18.0.0'} + deprecated: Please upgrade your lockfile to use the latest 3.x version of @smithy/core for various fixes, see https://github.com/smithy-lang/smithy-typescript/blob/main/packages/core/CHANGELOG.md + '@smithy/core@3.18.4': resolution: {integrity: sha512-o5tMqPZILBvvROfC8vC+dSVnWJl9a0u9ax1i1+Bq8515eYjUJqqk5XjjEsDLoeL5dSqGSh6WGdVx1eJ1E/Nwhw==} engines: {node: '>=18.0.0'} deprecated: Please upgrade your lockfile to use the latest 3.x version of @smithy/core for various fixes, see https://github.com/smithy-lang/smithy-typescript/blob/main/packages/core/CHANGELOG.md - + '@smithy/core@3.18.5': resolution: {integrity: sha512-6gnIz3h+PEPQGDj8MnRSjDvKBah042jEoPgjFGJ4iJLBE78L4lY/n98x14XyPF4u3lN179Ub/ZKFY5za9GeLQw==} engines: {node: '>=18.0.0'} @@ -15713,6 +15721,8 @@ snapshots: '@ckeditor/ckeditor5-core': 47.2.0 '@ckeditor/ckeditor5-utils': 47.2.0 ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-code-block@47.2.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)': dependencies: @@ -15777,8 +15787,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.2.0 '@ckeditor/ckeditor5-watchdog': 47.2.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-dev-build-tools@43.1.0(@swc/helpers@0.5.17)(tslib@2.8.1)(typescript@5.9.3)': dependencies: