diff --git a/surfsense_web/components/ui/block-draggable.tsx b/surfsense_web/components/ui/block-draggable.tsx
index f148e38c8..21405ebff 100644
--- a/surfsense_web/components/ui/block-draggable.tsx
+++ b/surfsense_web/components/ui/block-draggable.tsx
@@ -1,513 +1,467 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import { DndPlugin, useDraggable, useDropLine } from '@platejs/dnd';
-import { expandListItemsWithChildren } from '@platejs/list';
-import { BlockSelectionPlugin } from '@platejs/selection/react';
-import { GripVertical } from 'lucide-react';
-import { type TElement, getPluginByType, isType, KEYS } from 'platejs';
+import { DndPlugin, useDraggable, useDropLine } from "@platejs/dnd";
+import { expandListItemsWithChildren } from "@platejs/list";
+import { BlockSelectionPlugin } from "@platejs/selection/react";
+import { GripVertical } from "lucide-react";
+import { type TElement, getPluginByType, isType, KEYS } from "platejs";
import {
- type PlateEditor,
- type PlateElementProps,
- type RenderNodeWrapper,
- MemoizedChildren,
- useEditorRef,
- useElement,
- usePluginOption,
-} from 'platejs/react';
-import { useSelected } from 'platejs/react';
+ type PlateEditor,
+ type PlateElementProps,
+ type RenderNodeWrapper,
+ MemoizedChildren,
+ useEditorRef,
+ useElement,
+ usePluginOption,
+} from "platejs/react";
+import { useSelected } from "platejs/react";
-import { Button } from '@/components/ui/button';
-import {
- Tooltip,
- TooltipContent,
- TooltipTrigger,
-} from '@/components/ui/tooltip';
-import { cn } from '@/lib/utils';
+import { Button } from "@/components/ui/button";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
+import { cn } from "@/lib/utils";
const UNDRAGGABLE_KEYS = [KEYS.column, KEYS.tr, KEYS.td];
export const BlockDraggable: RenderNodeWrapper = (props) => {
- const { editor, element, path } = props;
+ const { editor, element, path } = props;
- const enabled = React.useMemo(() => {
- if (editor.dom.readOnly) return false;
+ const enabled = React.useMemo(() => {
+ if (editor.dom.readOnly) return false;
- if (path.length === 1 && !isType(editor, element, UNDRAGGABLE_KEYS)) {
- return true;
- }
- if (path.length === 3 && !isType(editor, element, UNDRAGGABLE_KEYS)) {
- const block = editor.api.some({
- at: path,
- match: {
- type: editor.getType(KEYS.column),
- },
- });
+ if (path.length === 1 && !isType(editor, element, UNDRAGGABLE_KEYS)) {
+ return true;
+ }
+ if (path.length === 3 && !isType(editor, element, UNDRAGGABLE_KEYS)) {
+ const block = editor.api.some({
+ at: path,
+ match: {
+ type: editor.getType(KEYS.column),
+ },
+ });
- if (block) {
- return true;
- }
- }
- if (path.length === 4 && !isType(editor, element, UNDRAGGABLE_KEYS)) {
- const block = editor.api.some({
- at: path,
- match: {
- type: editor.getType(KEYS.table),
- },
- });
+ if (block) {
+ return true;
+ }
+ }
+ if (path.length === 4 && !isType(editor, element, UNDRAGGABLE_KEYS)) {
+ const block = editor.api.some({
+ at: path,
+ match: {
+ type: editor.getType(KEYS.table),
+ },
+ });
- if (block) {
- return true;
- }
- }
+ if (block) {
+ return true;
+ }
+ }
- return false;
- }, [editor, element, path]);
+ return false;
+ }, [editor, element, path]);
- if (!enabled) return;
+ if (!enabled) return;
- return (props) =>
;
+ return (props) =>
;
};
function Draggable(props: PlateElementProps) {
- const { children, editor, element, path } = props;
- const blockSelectionApi = editor.getApi(BlockSelectionPlugin).blockSelection;
+ const { children, editor, element, path } = props;
+ const blockSelectionApi = editor.getApi(BlockSelectionPlugin).blockSelection;
- const { isAboutToDrag, isDragging, nodeRef, previewRef, handleRef } =
- useDraggable({
- element,
- onDropHandler: (_, { dragItem }) => {
- const id = (dragItem as { id: string[] | string }).id;
+ const { isAboutToDrag, isDragging, nodeRef, previewRef, handleRef } = useDraggable({
+ element,
+ onDropHandler: (_, { dragItem }) => {
+ const id = (dragItem as { id: string[] | string }).id;
- if (blockSelectionApi) {
- blockSelectionApi.add(id);
- }
- resetPreview();
- },
- });
+ if (blockSelectionApi) {
+ blockSelectionApi.add(id);
+ }
+ resetPreview();
+ },
+ });
- const isInColumn = path.length === 3;
- const isInTable = path.length === 4;
+ const isInColumn = path.length === 3;
+ const isInTable = path.length === 4;
- const [previewTop, setPreviewTop] = React.useState(0);
+ const [previewTop, setPreviewTop] = React.useState(0);
- const resetPreview = () => {
- if (previewRef.current) {
- previewRef.current.replaceChildren();
- previewRef.current?.classList.add('hidden');
- }
- };
+ const resetPreview = () => {
+ if (previewRef.current) {
+ previewRef.current.replaceChildren();
+ previewRef.current?.classList.add("hidden");
+ }
+ };
- // clear up virtual multiple preview when drag end
- React.useEffect(() => {
- if (!isDragging) {
- resetPreview();
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [isDragging]);
+ // clear up virtual multiple preview when drag end
+ React.useEffect(() => {
+ if (!isDragging) {
+ resetPreview();
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isDragging]);
- React.useEffect(() => {
- if (isAboutToDrag) {
- previewRef.current?.classList.remove('opacity-0');
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [isAboutToDrag]);
+ React.useEffect(() => {
+ if (isAboutToDrag) {
+ previewRef.current?.classList.remove("opacity-0");
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isAboutToDrag]);
- const [dragButtonTop, setDragButtonTop] = React.useState(0);
+ const [dragButtonTop, setDragButtonTop] = React.useState(0);
- return (
-
{
- if (isDragging) return;
- setDragButtonTop(calcDragButtonTop(editor, element));
- }}
- >
- {!isInTable && (
-
-
-
- )}
+ return (
+
{
+ if (isDragging) return;
+ setDragButtonTop(calcDragButtonTop(editor, element));
+ }}
+ >
+ {!isInTable && (
+
+
+
+ )}
-
+
-
- editor
- .getApi(BlockSelectionPlugin)
- .blockSelection.addOnContextMenu({ element, event })
- }
- >
- {children}
-
-
-
- );
+
+ editor.getApi(BlockSelectionPlugin).blockSelection.addOnContextMenu({ element, event })
+ }
+ >
+ {children}
+
+
+
+ );
}
-function Gutter({
- children,
- className,
- ...props
-}: React.ComponentProps<'div'>) {
- const editor = useEditorRef();
- const element = useElement();
- const isSelectionAreaVisible = usePluginOption(
- BlockSelectionPlugin,
- 'isSelectionAreaVisible'
- );
- const selected = useSelected();
+function Gutter({ children, className, ...props }: React.ComponentProps<"div">) {
+ const editor = useEditorRef();
+ const element = useElement();
+ const isSelectionAreaVisible = usePluginOption(BlockSelectionPlugin, "isSelectionAreaVisible");
+ const selected = useSelected();
- return (
-
- {children}
-
- );
+ return (
+
+ {children}
+
+ );
}
const DragHandle = React.memo(function DragHandle({
- isDragging,
- previewRef,
- resetPreview,
- setPreviewTop,
+ isDragging,
+ previewRef,
+ resetPreview,
+ setPreviewTop,
}: {
- isDragging: boolean;
- previewRef: React.RefObject
;
- resetPreview: () => void;
- setPreviewTop: (top: number) => void;
+ isDragging: boolean;
+ previewRef: React.RefObject;
+ resetPreview: () => void;
+ setPreviewTop: (top: number) => void;
}) {
- const editor = useEditorRef();
- const element = useElement();
+ const editor = useEditorRef();
+ const element = useElement();
- return (
-
-
- {
- e.preventDefault();
- editor.getApi(BlockSelectionPlugin).blockSelection.focus();
- }}
- onMouseDown={(e) => {
- resetPreview();
+ return (
+
+
+ {
+ e.preventDefault();
+ editor.getApi(BlockSelectionPlugin).blockSelection.focus();
+ }}
+ onMouseDown={(e) => {
+ resetPreview();
- if ((e.button !== 0 && e.button !== 2) || e.shiftKey) return;
+ if ((e.button !== 0 && e.button !== 2) || e.shiftKey) return;
- const blockSelection = editor
- .getApi(BlockSelectionPlugin)
- .blockSelection.getNodes({ sort: true });
+ const blockSelection = editor
+ .getApi(BlockSelectionPlugin)
+ .blockSelection.getNodes({ sort: true });
- let selectionNodes =
- blockSelection.length > 0
- ? blockSelection
- : editor.api.blocks({ mode: 'highest' });
+ let selectionNodes =
+ blockSelection.length > 0 ? blockSelection : editor.api.blocks({ mode: "highest" });
- // If current block is not in selection, use it as the starting point
- if (!selectionNodes.some(([node]) => node.id === element.id)) {
- selectionNodes = [[element, editor.api.findPath(element)!]];
- }
+ // If current block is not in selection, use it as the starting point
+ if (!selectionNodes.some(([node]) => node.id === element.id)) {
+ selectionNodes = [[element, editor.api.findPath(element)!]];
+ }
- // Process selection nodes to include list children
- const blocks = expandListItemsWithChildren(
- editor,
- selectionNodes
- ).map(([node]) => node);
+ // Process selection nodes to include list children
+ const blocks = expandListItemsWithChildren(editor, selectionNodes).map(
+ ([node]) => node
+ );
- if (blockSelection.length === 0) {
- editor.tf.blur();
- editor.tf.collapse();
- }
+ if (blockSelection.length === 0) {
+ editor.tf.blur();
+ editor.tf.collapse();
+ }
- const elements = createDragPreviewElements(editor, blocks);
- previewRef.current?.append(...elements);
- previewRef.current?.classList.remove('hidden');
- previewRef.current?.classList.add('opacity-0');
- editor.setOption(DndPlugin, 'multiplePreviewRef', previewRef);
+ const elements = createDragPreviewElements(editor, blocks);
+ previewRef.current?.append(...elements);
+ previewRef.current?.classList.remove("hidden");
+ previewRef.current?.classList.add("opacity-0");
+ editor.setOption(DndPlugin, "multiplePreviewRef", previewRef);
- editor
- .getApi(BlockSelectionPlugin)
- .blockSelection.set(blocks.map((block) => block.id as string));
- }}
- onMouseEnter={() => {
- if (isDragging) return;
+ editor
+ .getApi(BlockSelectionPlugin)
+ .blockSelection.set(blocks.map((block) => block.id as string));
+ }}
+ onMouseEnter={() => {
+ if (isDragging) return;
- const blockSelection = editor
- .getApi(BlockSelectionPlugin)
- .blockSelection.getNodes({ sort: true });
+ const blockSelection = editor
+ .getApi(BlockSelectionPlugin)
+ .blockSelection.getNodes({ sort: true });
- let selectedBlocks =
- blockSelection.length > 0
- ? blockSelection
- : editor.api.blocks({ mode: 'highest' });
+ let selectedBlocks =
+ blockSelection.length > 0 ? blockSelection : editor.api.blocks({ mode: "highest" });
- // If current block is not in selection, use it as the starting point
- if (!selectedBlocks.some(([node]) => node.id === element.id)) {
- selectedBlocks = [[element, editor.api.findPath(element)!]];
- }
+ // If current block is not in selection, use it as the starting point
+ if (!selectedBlocks.some(([node]) => node.id === element.id)) {
+ selectedBlocks = [[element, editor.api.findPath(element)!]];
+ }
- // Process selection to include list children
- const processedBlocks = expandListItemsWithChildren(
- editor,
- selectedBlocks
- );
+ // Process selection to include list children
+ const processedBlocks = expandListItemsWithChildren(editor, selectedBlocks);
- const ids = processedBlocks.map((block) => block[0].id as string);
+ const ids = processedBlocks.map((block) => block[0].id as string);
- if (ids.length > 1 && ids.includes(element.id as string)) {
- const previewTop = calculatePreviewTop(editor, {
- blocks: processedBlocks.map((block) => block[0]),
- element,
- });
- setPreviewTop(previewTop);
- } else {
- setPreviewTop(0);
- }
- }}
- onMouseUp={() => {
- resetPreview();
- }}
- data-plate-prevent-deselect
- role="button"
- >
-
-
-
-
- );
+ if (ids.length > 1 && ids.includes(element.id as string)) {
+ const previewTop = calculatePreviewTop(editor, {
+ blocks: processedBlocks.map((block) => block[0]),
+ element,
+ });
+ setPreviewTop(previewTop);
+ } else {
+ setPreviewTop(0);
+ }
+ }}
+ onMouseUp={() => {
+ resetPreview();
+ }}
+ data-plate-prevent-deselect
+ role="button"
+ >
+
+
+
+
+ );
});
const DropLine = React.memo(function DropLine({
- className,
- ...props
-}: React.ComponentProps<'div'>) {
- const { dropLine } = useDropLine();
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ const { dropLine } = useDropLine();
- if (!dropLine) return null;
+ if (!dropLine) return null;
- return (
-
- );
+ return (
+
+ );
});
-const createDragPreviewElements = (
- editor: PlateEditor,
- blocks: TElement[]
-): HTMLElement[] => {
- const elements: HTMLElement[] = [];
- const ids: string[] = [];
+const createDragPreviewElements = (editor: PlateEditor, blocks: TElement[]): HTMLElement[] => {
+ const elements: HTMLElement[] = [];
+ const ids: string[] = [];
- /**
- * Remove data attributes from the element to avoid recognized as slate
- * elements incorrectly.
- */
- const removeDataAttributes = (element: HTMLElement) => {
- Array.from(element.attributes).forEach((attr) => {
- if (
- attr.name.startsWith('data-slate') ||
- attr.name.startsWith('data-block-id')
- ) {
- element.removeAttribute(attr.name);
- }
- });
+ /**
+ * Remove data attributes from the element to avoid recognized as slate
+ * elements incorrectly.
+ */
+ const removeDataAttributes = (element: HTMLElement) => {
+ Array.from(element.attributes).forEach((attr) => {
+ if (attr.name.startsWith("data-slate") || attr.name.startsWith("data-block-id")) {
+ element.removeAttribute(attr.name);
+ }
+ });
- Array.from(element.children).forEach((child) => {
- removeDataAttributes(child as HTMLElement);
- });
- };
+ Array.from(element.children).forEach((child) => {
+ removeDataAttributes(child as HTMLElement);
+ });
+ };
- const resolveElement = (node: TElement, index: number) => {
- const domNode = editor.api.toDOMNode(node)!;
- const newDomNode = domNode.cloneNode(true) as HTMLElement;
+ const resolveElement = (node: TElement, index: number) => {
+ const domNode = editor.api.toDOMNode(node)!;
+ const newDomNode = domNode.cloneNode(true) as HTMLElement;
- // Apply visual compensation for horizontal scroll
- const applyScrollCompensation = (
- original: Element,
- cloned: HTMLElement
- ) => {
- const scrollLeft = original.scrollLeft;
+ // Apply visual compensation for horizontal scroll
+ const applyScrollCompensation = (original: Element, cloned: HTMLElement) => {
+ const scrollLeft = original.scrollLeft;
- if (scrollLeft > 0) {
- // Create a wrapper to handle the scroll offset
- const scrollWrapper = document.createElement('div');
- scrollWrapper.style.overflow = 'hidden';
- scrollWrapper.style.width = `${original.clientWidth}px`;
+ if (scrollLeft > 0) {
+ // Create a wrapper to handle the scroll offset
+ const scrollWrapper = document.createElement("div");
+ scrollWrapper.style.overflow = "hidden";
+ scrollWrapper.style.width = `${original.clientWidth}px`;
- // Create inner container with the full content
- const innerContainer = document.createElement('div');
- innerContainer.style.transform = `translateX(-${scrollLeft}px)`;
- innerContainer.style.width = `${original.scrollWidth}px`;
+ // Create inner container with the full content
+ const innerContainer = document.createElement("div");
+ innerContainer.style.transform = `translateX(-${scrollLeft}px)`;
+ innerContainer.style.width = `${original.scrollWidth}px`;
- // Move all children to the inner container
- while (cloned.firstChild) {
- innerContainer.append(cloned.firstChild);
- }
+ // Move all children to the inner container
+ while (cloned.firstChild) {
+ innerContainer.append(cloned.firstChild);
+ }
- // Apply the original element's styles to maintain appearance
- const originalStyles = window.getComputedStyle(original);
- cloned.style.padding = '0';
- innerContainer.style.padding = originalStyles.padding;
+ // Apply the original element's styles to maintain appearance
+ const originalStyles = window.getComputedStyle(original);
+ cloned.style.padding = "0";
+ innerContainer.style.padding = originalStyles.padding;
- scrollWrapper.append(innerContainer);
- cloned.append(scrollWrapper);
- }
- };
+ scrollWrapper.append(innerContainer);
+ cloned.append(scrollWrapper);
+ }
+ };
- applyScrollCompensation(domNode, newDomNode);
+ applyScrollCompensation(domNode, newDomNode);
- ids.push(node.id as string);
- const wrapper = document.createElement('div');
- wrapper.append(newDomNode);
- wrapper.style.display = 'flow-root';
+ ids.push(node.id as string);
+ const wrapper = document.createElement("div");
+ wrapper.append(newDomNode);
+ wrapper.style.display = "flow-root";
- const lastDomNode = blocks[index - 1];
+ const lastDomNode = blocks[index - 1];
- if (lastDomNode) {
- const lastDomNodeRect = editor.api
- .toDOMNode(lastDomNode)!
- .parentElement!.getBoundingClientRect();
+ if (lastDomNode) {
+ const lastDomNodeRect = editor.api
+ .toDOMNode(lastDomNode)!
+ .parentElement!.getBoundingClientRect();
- const domNodeRect = domNode.parentElement!.getBoundingClientRect();
+ const domNodeRect = domNode.parentElement!.getBoundingClientRect();
- const distance = domNodeRect.top - lastDomNodeRect.bottom;
+ const distance = domNodeRect.top - lastDomNodeRect.bottom;
- // Check if the two elements are adjacent (touching each other)
- if (distance > 15) {
- wrapper.style.marginTop = `${distance}px`;
- }
- }
+ // Check if the two elements are adjacent (touching each other)
+ if (distance > 15) {
+ wrapper.style.marginTop = `${distance}px`;
+ }
+ }
- removeDataAttributes(newDomNode);
- elements.push(wrapper);
- };
+ removeDataAttributes(newDomNode);
+ elements.push(wrapper);
+ };
- blocks.forEach((node, index) => {
- resolveElement(node, index);
- });
+ blocks.forEach((node, index) => {
+ resolveElement(node, index);
+ });
- editor.setOption(DndPlugin, 'draggingId', ids);
+ editor.setOption(DndPlugin, "draggingId", ids);
- return elements;
+ return elements;
};
const calculatePreviewTop = (
- editor: PlateEditor,
- {
- blocks,
- element,
- }: {
- blocks: TElement[];
- element: TElement;
- }
+ editor: PlateEditor,
+ {
+ blocks,
+ element,
+ }: {
+ blocks: TElement[];
+ element: TElement;
+ }
): number => {
- const child = editor.api.toDOMNode(element)!;
- const editable = editor.api.toDOMNode(editor)!;
- const firstSelectedChild = blocks[0];
+ const child = editor.api.toDOMNode(element)!;
+ const editable = editor.api.toDOMNode(editor)!;
+ const firstSelectedChild = blocks[0];
- const firstDomNode = editor.api.toDOMNode(firstSelectedChild)!;
- // Get editor's top padding
- const editorPaddingTop = Number(
- window.getComputedStyle(editable).paddingTop.replace('px', '')
- );
+ const firstDomNode = editor.api.toDOMNode(firstSelectedChild)!;
+ // Get editor's top padding
+ const editorPaddingTop = Number(window.getComputedStyle(editable).paddingTop.replace("px", ""));
- // Calculate distance from first selected node to editor top
- const firstNodeToEditorDistance =
- firstDomNode.getBoundingClientRect().top -
- editable.getBoundingClientRect().top -
- editorPaddingTop;
+ // Calculate distance from first selected node to editor top
+ const firstNodeToEditorDistance =
+ firstDomNode.getBoundingClientRect().top -
+ editable.getBoundingClientRect().top -
+ editorPaddingTop;
- // Get margin top of first selected node
- const firstMarginTopString = window.getComputedStyle(firstDomNode).marginTop;
- const marginTop = Number(firstMarginTopString.replace('px', ''));
+ // Get margin top of first selected node
+ const firstMarginTopString = window.getComputedStyle(firstDomNode).marginTop;
+ const marginTop = Number(firstMarginTopString.replace("px", ""));
- // Calculate distance from current node to editor top
- const currentToEditorDistance =
- child.getBoundingClientRect().top -
- editable.getBoundingClientRect().top -
- editorPaddingTop;
+ // Calculate distance from current node to editor top
+ const currentToEditorDistance =
+ child.getBoundingClientRect().top - editable.getBoundingClientRect().top - editorPaddingTop;
- const currentMarginTopString = window.getComputedStyle(child).marginTop;
- const currentMarginTop = Number(currentMarginTopString.replace('px', ''));
+ const currentMarginTopString = window.getComputedStyle(child).marginTop;
+ const currentMarginTop = Number(currentMarginTopString.replace("px", ""));
- const previewElementsTopDistance =
- currentToEditorDistance -
- firstNodeToEditorDistance +
- marginTop -
- currentMarginTop;
+ const previewElementsTopDistance =
+ currentToEditorDistance - firstNodeToEditorDistance + marginTop - currentMarginTop;
- return previewElementsTopDistance;
+ return previewElementsTopDistance;
};
const calcDragButtonTop = (editor: PlateEditor, element: TElement): number => {
- const child = editor.api.toDOMNode(element)!;
+ const child = editor.api.toDOMNode(element)!;
- const currentMarginTopString = window.getComputedStyle(child).marginTop;
- const currentMarginTop = Number(currentMarginTopString.replace('px', ''));
+ const currentMarginTopString = window.getComputedStyle(child).marginTop;
+ const currentMarginTop = Number(currentMarginTopString.replace("px", ""));
- return currentMarginTop;
+ return currentMarginTop;
};
diff --git a/surfsense_web/components/ui/block-list.tsx b/surfsense_web/components/ui/block-list.tsx
index 6cf966327..88bfc0d37 100644
--- a/surfsense_web/components/ui/block-list.tsx
+++ b/surfsense_web/components/ui/block-list.tsx
@@ -1,87 +1,72 @@
-'use client';
+"use client";
-import React from 'react';
+import React from "react";
-import type { TListElement } from 'platejs';
+import type { TListElement } from "platejs";
-import { isOrderedList } from '@platejs/list';
-import {
- useTodoListElement,
- useTodoListElementState,
-} from '@platejs/list/react';
-import {
- type PlateElementProps,
- type RenderNodeWrapper,
- useReadOnly,
-} from 'platejs/react';
+import { isOrderedList } from "@platejs/list";
+import { useTodoListElement, useTodoListElementState } from "@platejs/list/react";
+import { type PlateElementProps, type RenderNodeWrapper, useReadOnly } from "platejs/react";
-import { Checkbox } from '@/components/ui/checkbox';
-import { cn } from '@/lib/utils';
+import { Checkbox } from "@/components/ui/checkbox";
+import { cn } from "@/lib/utils";
const config: Record<
- string,
- {
- Li: React.FC;
- Marker: React.FC;
- }
+ string,
+ {
+ Li: React.FC;
+ Marker: React.FC;
+ }
> = {
- todo: {
- Li: TodoLi,
- Marker: TodoMarker,
- },
+ todo: {
+ Li: TodoLi,
+ Marker: TodoMarker,
+ },
};
export const BlockList: RenderNodeWrapper = (props) => {
- if (!props.element.listStyleType) return;
+ if (!props.element.listStyleType) return;
- return (props) =>
;
+ return (props) =>
;
};
function List(props: PlateElementProps) {
- const { listStart, listStyleType } = props.element as TListElement;
- const { Li, Marker } = config[listStyleType] ?? {};
- const List = isOrderedList(props.element) ? 'ol' : 'ul';
+ const { listStart, listStyleType } = props.element as TListElement;
+ const { Li, Marker } = config[listStyleType] ?? {};
+ const List = isOrderedList(props.element) ? "ol" : "ul";
- return (
-
- {Marker && }
- {Li ? : {props.children}}
-
- );
+ return (
+
+ {Marker && }
+ {Li ? : {props.children}}
+
+ );
}
function TodoMarker(props: PlateElementProps) {
- const state = useTodoListElementState({ element: props.element });
- const { checkboxProps } = useTodoListElement(state);
- const readOnly = useReadOnly();
+ const state = useTodoListElementState({ element: props.element });
+ const { checkboxProps } = useTodoListElement(state);
+ const readOnly = useReadOnly();
- return (
-
-
-
- );
+ return (
+
+
+
+ );
}
function TodoLi(props: PlateElementProps) {
- return (
-
- {props.children}
-
- );
+ return (
+
+ {props.children}
+
+ );
}
diff --git a/surfsense_web/components/ui/block-selection.tsx b/surfsense_web/components/ui/block-selection.tsx
index 386fe8852..b8ffdda15 100644
--- a/surfsense_web/components/ui/block-selection.tsx
+++ b/surfsense_web/components/ui/block-selection.tsx
@@ -1,44 +1,39 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import { DndPlugin } from '@platejs/dnd';
-import { useBlockSelected } from '@platejs/selection/react';
-import { cva } from 'class-variance-authority';
-import { type PlateElementProps, usePluginOption } from 'platejs/react';
+import { DndPlugin } from "@platejs/dnd";
+import { useBlockSelected } from "@platejs/selection/react";
+import { cva } from "class-variance-authority";
+import { type PlateElementProps, usePluginOption } from "platejs/react";
export const blockSelectionVariants = cva(
- 'pointer-events-none absolute inset-0 z-1 bg-brand/[.13] transition-opacity',
- {
- defaultVariants: {
- active: true,
- },
- variants: {
- active: {
- false: 'opacity-0',
- true: 'opacity-100',
- },
- },
- }
+ "pointer-events-none absolute inset-0 z-1 bg-brand/[.13] transition-opacity",
+ {
+ defaultVariants: {
+ active: true,
+ },
+ variants: {
+ active: {
+ false: "opacity-0",
+ true: "opacity-100",
+ },
+ },
+ }
);
export function BlockSelection(props: PlateElementProps) {
- const isBlockSelected = useBlockSelected();
- const isDragging = usePluginOption(DndPlugin, 'isDragging');
+ const isBlockSelected = useBlockSelected();
+ const isDragging = usePluginOption(DndPlugin, "isDragging");
- if (
- !isBlockSelected ||
- props.plugin.key === 'tr' ||
- props.plugin.key === 'table'
- )
- return null;
+ if (!isBlockSelected || props.plugin.key === "tr" || props.plugin.key === "table") return null;
- return (
-
- );
+ return (
+
+ );
}
diff --git a/surfsense_web/components/ui/blockquote-node.tsx b/surfsense_web/components/ui/blockquote-node.tsx
index ba5bec4e8..1de9b19cf 100644
--- a/surfsense_web/components/ui/blockquote-node.tsx
+++ b/surfsense_web/components/ui/blockquote-node.tsx
@@ -1,13 +1,7 @@
-'use client';
+"use client";
-import { type PlateElementProps, PlateElement } from 'platejs/react';
+import { type PlateElementProps, PlateElement } from "platejs/react";
export function BlockquoteElement(props: PlateElementProps) {
- return (
-
- );
+ return ;
}
diff --git a/surfsense_web/components/ui/callout-node.tsx b/surfsense_web/components/ui/callout-node.tsx
index 2d24cb864..0972fdf7f 100644
--- a/surfsense_web/components/ui/callout-node.tsx
+++ b/surfsense_web/components/ui/callout-node.tsx
@@ -1,83 +1,77 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { TCalloutElement } from 'platejs';
+import type { TCalloutElement } from "platejs";
-import { CalloutPlugin } from '@platejs/callout/react';
-import { cva } from 'class-variance-authority';
-import { type PlateElementProps, PlateElement, useEditorPlugin } from 'platejs/react';
+import { CalloutPlugin } from "@platejs/callout/react";
+import { cva } from "class-variance-authority";
+import { type PlateElementProps, PlateElement, useEditorPlugin } from "platejs/react";
-import { cn } from '@/lib/utils';
+import { cn } from "@/lib/utils";
-const calloutVariants = cva(
- 'my-1 flex w-full items-start gap-2 rounded-lg border p-4',
- {
- defaultVariants: {
- variant: 'info',
- },
- variants: {
- variant: {
- info: 'border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/50',
- warning: 'border-yellow-200 bg-yellow-50 dark:border-yellow-800 dark:bg-yellow-950/50',
- error: 'border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950/50',
- success: 'border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950/50',
- note: 'border-muted bg-muted/50',
- tip: 'border-purple-200 bg-purple-50 dark:border-purple-800 dark:bg-purple-950/50',
- },
- },
- }
-);
+const calloutVariants = cva("my-1 flex w-full items-start gap-2 rounded-lg border p-4", {
+ defaultVariants: {
+ variant: "info",
+ },
+ variants: {
+ variant: {
+ info: "border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/50",
+ warning: "border-yellow-200 bg-yellow-50 dark:border-yellow-800 dark:bg-yellow-950/50",
+ error: "border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950/50",
+ success: "border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950/50",
+ note: "border-muted bg-muted/50",
+ tip: "border-purple-200 bg-purple-50 dark:border-purple-800 dark:bg-purple-950/50",
+ },
+ },
+});
const calloutIcons: Record = {
- info: '💡',
- warning: '⚠️',
- error: '🚨',
- success: '✅',
- note: '📝',
- tip: '💜',
+ info: "💡",
+ warning: "⚠️",
+ error: "🚨",
+ success: "✅",
+ note: "📝",
+ tip: "💜",
};
-const variantCycle = ['info', 'warning', 'error', 'success', 'note', 'tip'] as const;
+const variantCycle = ["info", "warning", "error", "success", "note", "tip"] as const;
-export function CalloutElement({
- children,
- ...props
-}: PlateElementProps) {
- const { editor } = useEditorPlugin(CalloutPlugin);
- const element = props.element;
- const variant = element.variant || 'info';
- const icon = element.icon || calloutIcons[variant] || '💡';
+export function CalloutElement({ children, ...props }: PlateElementProps) {
+ const { editor } = useEditorPlugin(CalloutPlugin);
+ const element = props.element;
+ const variant = element.variant || "info";
+ const icon = element.icon || calloutIcons[variant] || "💡";
- const cycleVariant = React.useCallback(() => {
- const currentIndex = variantCycle.indexOf(variant as (typeof variantCycle)[number]);
- const nextIndex = (currentIndex + 1) % variantCycle.length;
- const nextVariant = variantCycle[nextIndex];
+ const cycleVariant = React.useCallback(() => {
+ const currentIndex = variantCycle.indexOf(variant as (typeof variantCycle)[number]);
+ const nextIndex = (currentIndex + 1) % variantCycle.length;
+ const nextVariant = variantCycle[nextIndex];
- editor.tf.setNodes(
- {
- variant: nextVariant,
- icon: calloutIcons[nextVariant],
- },
- { at: props.path }
- );
- }, [editor, variant, props.path]);
+ editor.tf.setNodes(
+ {
+ variant: nextVariant,
+ icon: calloutIcons[nextVariant],
+ },
+ { at: props.path }
+ );
+ }, [editor, variant, props.path]);
- return (
-
-
- {children}
-
- );
+ return (
+
+
+ {children}
+
+ );
}
diff --git a/surfsense_web/components/ui/checkbox.tsx b/surfsense_web/components/ui/checkbox.tsx
index a3ec18410..465783c79 100644
--- a/surfsense_web/components/ui/checkbox.tsx
+++ b/surfsense_web/components/ui/checkbox.tsx
@@ -1,32 +1,29 @@
-"use client"
+"use client";
-import * as React from "react"
-import { CheckIcon } from "lucide-react"
-import { Checkbox as CheckboxPrimitive } from "radix-ui"
+import * as React from "react";
+import { CheckIcon } from "lucide-react";
+import { Checkbox as CheckboxPrimitive } from "radix-ui";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
-function Checkbox({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
-
-
- )
+function Checkbox({ className, ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+ );
}
-export { Checkbox }
+export { Checkbox };
diff --git a/surfsense_web/components/ui/code-block-node.tsx b/surfsense_web/components/ui/code-block-node.tsx
index b846297db..f0f1c6db7 100644
--- a/surfsense_web/components/ui/code-block-node.tsx
+++ b/surfsense_web/components/ui/code-block-node.tsx
@@ -1,289 +1,264 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import { formatCodeBlock, isLangSupported } from '@platejs/code-block';
-import { BracesIcon, Check, CheckIcon, CopyIcon } from 'lucide-react';
-import { type TCodeBlockElement, type TCodeSyntaxLeaf, NodeApi } from 'platejs';
+import { formatCodeBlock, isLangSupported } from "@platejs/code-block";
+import { BracesIcon, Check, CheckIcon, CopyIcon } from "lucide-react";
+import { type TCodeBlockElement, type TCodeSyntaxLeaf, NodeApi } from "platejs";
import {
- type PlateElementProps,
- type PlateLeafProps,
- PlateElement,
- PlateLeaf,
-} from 'platejs/react';
-import { useEditorRef, useElement, useReadOnly } from 'platejs/react';
+ type PlateElementProps,
+ type PlateLeafProps,
+ PlateElement,
+ PlateLeaf,
+} from "platejs/react";
+import { useEditorRef, useElement, useReadOnly } from "platejs/react";
-import { Button } from '@/components/ui/button';
+import { Button } from "@/components/ui/button";
import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
-} from '@/components/ui/command';
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from '@/components/ui/popover';
-import { cn } from '@/lib/utils';
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/components/ui/command";
+import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
+import { cn } from "@/lib/utils";
export function CodeBlockElement(props: PlateElementProps) {
- const { editor, element } = props;
+ const { editor, element } = props;
- return (
-
-
-
- {props.children}
-
+ return (
+
+
+
+ {props.children}
+
-
- {isLangSupported(element.lang) && (
-
- )}
+
+ {isLangSupported(element.lang) && (
+
+ )}
-
+
- NodeApi.string(element)}
- />
-
-
-
- );
+
NodeApi.string(element)}
+ />
+
+
+
+ );
}
function CodeBlockCombobox() {
- const [open, setOpen] = React.useState(false);
- const readOnly = useReadOnly();
- const editor = useEditorRef();
- const element = useElement();
- const value = element.lang || 'plaintext';
- const [searchValue, setSearchValue] = React.useState('');
+ const [open, setOpen] = React.useState(false);
+ const readOnly = useReadOnly();
+ const editor = useEditorRef();
+ const element = useElement();
+ const value = element.lang || "plaintext";
+ const [searchValue, setSearchValue] = React.useState("");
- const items = React.useMemo(
- () =>
- languages.filter(
- (language) =>
- !searchValue ||
- language.label.toLowerCase().includes(searchValue.toLowerCase())
- ),
- [searchValue]
- );
+ const items = React.useMemo(
+ () =>
+ languages.filter(
+ (language) =>
+ !searchValue || language.label.toLowerCase().includes(searchValue.toLowerCase())
+ ),
+ [searchValue]
+ );
- if (readOnly) return null;
+ if (readOnly) return null;
- return (
-
-
-
-
- setSearchValue('')}
- >
-
- setSearchValue(value)}
- placeholder="Search language..."
- />
- No language found.
+ return (
+
+
+
+
+ setSearchValue("")}>
+
+ setSearchValue(value)}
+ placeholder="Search language..."
+ />
+ No language found.
-
-
- {items.map((language) => (
- {
- editor.tf.setNodes(
- { lang: value },
- { at: element }
- );
- setSearchValue(value);
- setOpen(false);
- }}
- >
-
- {language.label}
-
- ))}
-
-
-
-
-
- );
+
+
+ {items.map((language) => (
+ {
+ editor.tf.setNodes({ lang: value }, { at: element });
+ setSearchValue(value);
+ setOpen(false);
+ }}
+ >
+
+ {language.label}
+
+ ))}
+
+
+
+
+
+ );
}
function CopyButton({
- value,
- ...props
-}: { value: (() => string) | string } & Omit<
- React.ComponentProps,
- 'value'
->) {
- const [hasCopied, setHasCopied] = React.useState(false);
+ value,
+ ...props
+}: { value: (() => string) | string } & Omit, "value">) {
+ const [hasCopied, setHasCopied] = React.useState(false);
- React.useEffect(() => {
- setTimeout(() => {
- setHasCopied(false);
- }, 2000);
- }, [hasCopied]);
+ React.useEffect(() => {
+ setTimeout(() => {
+ setHasCopied(false);
+ }, 2000);
+ }, [hasCopied]);
- return (
-
- );
+ return (
+
+ );
}
export function CodeLineElement(props: PlateElementProps) {
- return ;
+ return ;
}
export function CodeSyntaxLeaf(props: PlateLeafProps) {
- const tokenClassName = props.leaf.className as string;
+ const tokenClassName = props.leaf.className as string;
- return ;
+ return ;
}
const languages: { label: string; value: string }[] = [
- { label: 'Auto', value: 'auto' },
- { label: 'Plain Text', value: 'plaintext' },
- { label: 'ABAP', value: 'abap' },
- { label: 'Agda', value: 'agda' },
- { label: 'Arduino', value: 'arduino' },
- { label: 'ASCII Art', value: 'ascii' },
- { label: 'Assembly', value: 'x86asm' },
- { label: 'Bash', value: 'bash' },
- { label: 'BASIC', value: 'basic' },
- { label: 'BNF', value: 'bnf' },
- { label: 'C', value: 'c' },
- { label: 'C#', value: 'csharp' },
- { label: 'C++', value: 'cpp' },
- { label: 'Clojure', value: 'clojure' },
- { label: 'CoffeeScript', value: 'coffeescript' },
- { label: 'Coq', value: 'coq' },
- { label: 'CSS', value: 'css' },
- { label: 'Dart', value: 'dart' },
- { label: 'Dhall', value: 'dhall' },
- { label: 'Diff', value: 'diff' },
- { label: 'Docker', value: 'dockerfile' },
- { label: 'EBNF', value: 'ebnf' },
- { label: 'Elixir', value: 'elixir' },
- { label: 'Elm', value: 'elm' },
- { label: 'Erlang', value: 'erlang' },
- { label: 'F#', value: 'fsharp' },
- { label: 'Flow', value: 'flow' },
- { label: 'Fortran', value: 'fortran' },
- { label: 'Gherkin', value: 'gherkin' },
- { label: 'GLSL', value: 'glsl' },
- { label: 'Go', value: 'go' },
- { label: 'GraphQL', value: 'graphql' },
- { label: 'Groovy', value: 'groovy' },
- { label: 'Haskell', value: 'haskell' },
- { label: 'HCL', value: 'hcl' },
- { label: 'HTML', value: 'html' },
- { label: 'Idris', value: 'idris' },
- { label: 'Java', value: 'java' },
- { label: 'JavaScript', value: 'javascript' },
- { label: 'JSON', value: 'json' },
- { label: 'Julia', value: 'julia' },
- { label: 'Kotlin', value: 'kotlin' },
- { label: 'LaTeX', value: 'latex' },
- { label: 'Less', value: 'less' },
- { label: 'Lisp', value: 'lisp' },
- { label: 'LiveScript', value: 'livescript' },
- { label: 'LLVM IR', value: 'llvm' },
- { label: 'Lua', value: 'lua' },
- { label: 'Makefile', value: 'makefile' },
- { label: 'Markdown', value: 'markdown' },
- { label: 'Markup', value: 'markup' },
- { label: 'MATLAB', value: 'matlab' },
- { label: 'Mathematica', value: 'mathematica' },
- { label: 'Mermaid', value: 'mermaid' },
- { label: 'Nix', value: 'nix' },
- { label: 'Notion Formula', value: 'notion' },
- { label: 'Objective-C', value: 'objectivec' },
- { label: 'OCaml', value: 'ocaml' },
- { label: 'Pascal', value: 'pascal' },
- { label: 'Perl', value: 'perl' },
- { label: 'PHP', value: 'php' },
- { label: 'PowerShell', value: 'powershell' },
- { label: 'Prolog', value: 'prolog' },
- { label: 'Protocol Buffers', value: 'protobuf' },
- { label: 'PureScript', value: 'purescript' },
- { label: 'Python', value: 'python' },
- { label: 'R', value: 'r' },
- { label: 'Racket', value: 'racket' },
- { label: 'Reason', value: 'reasonml' },
- { label: 'Ruby', value: 'ruby' },
- { label: 'Rust', value: 'rust' },
- { label: 'Sass', value: 'scss' },
- { label: 'Scala', value: 'scala' },
- { label: 'Scheme', value: 'scheme' },
- { label: 'SCSS', value: 'scss' },
- { label: 'Shell', value: 'shell' },
- { label: 'Smalltalk', value: 'smalltalk' },
- { label: 'Solidity', value: 'solidity' },
- { label: 'SQL', value: 'sql' },
- { label: 'Swift', value: 'swift' },
- { label: 'TOML', value: 'toml' },
- { label: 'TypeScript', value: 'typescript' },
- { label: 'VB.Net', value: 'vbnet' },
- { label: 'Verilog', value: 'verilog' },
- { label: 'VHDL', value: 'vhdl' },
- { label: 'Visual Basic', value: 'vbnet' },
- { label: 'WebAssembly', value: 'wasm' },
- { label: 'XML', value: 'xml' },
- { label: 'YAML', value: 'yaml' },
+ { label: "Auto", value: "auto" },
+ { label: "Plain Text", value: "plaintext" },
+ { label: "ABAP", value: "abap" },
+ { label: "Agda", value: "agda" },
+ { label: "Arduino", value: "arduino" },
+ { label: "ASCII Art", value: "ascii" },
+ { label: "Assembly", value: "x86asm" },
+ { label: "Bash", value: "bash" },
+ { label: "BASIC", value: "basic" },
+ { label: "BNF", value: "bnf" },
+ { label: "C", value: "c" },
+ { label: "C#", value: "csharp" },
+ { label: "C++", value: "cpp" },
+ { label: "Clojure", value: "clojure" },
+ { label: "CoffeeScript", value: "coffeescript" },
+ { label: "Coq", value: "coq" },
+ { label: "CSS", value: "css" },
+ { label: "Dart", value: "dart" },
+ { label: "Dhall", value: "dhall" },
+ { label: "Diff", value: "diff" },
+ { label: "Docker", value: "dockerfile" },
+ { label: "EBNF", value: "ebnf" },
+ { label: "Elixir", value: "elixir" },
+ { label: "Elm", value: "elm" },
+ { label: "Erlang", value: "erlang" },
+ { label: "F#", value: "fsharp" },
+ { label: "Flow", value: "flow" },
+ { label: "Fortran", value: "fortran" },
+ { label: "Gherkin", value: "gherkin" },
+ { label: "GLSL", value: "glsl" },
+ { label: "Go", value: "go" },
+ { label: "GraphQL", value: "graphql" },
+ { label: "Groovy", value: "groovy" },
+ { label: "Haskell", value: "haskell" },
+ { label: "HCL", value: "hcl" },
+ { label: "HTML", value: "html" },
+ { label: "Idris", value: "idris" },
+ { label: "Java", value: "java" },
+ { label: "JavaScript", value: "javascript" },
+ { label: "JSON", value: "json" },
+ { label: "Julia", value: "julia" },
+ { label: "Kotlin", value: "kotlin" },
+ { label: "LaTeX", value: "latex" },
+ { label: "Less", value: "less" },
+ { label: "Lisp", value: "lisp" },
+ { label: "LiveScript", value: "livescript" },
+ { label: "LLVM IR", value: "llvm" },
+ { label: "Lua", value: "lua" },
+ { label: "Makefile", value: "makefile" },
+ { label: "Markdown", value: "markdown" },
+ { label: "Markup", value: "markup" },
+ { label: "MATLAB", value: "matlab" },
+ { label: "Mathematica", value: "mathematica" },
+ { label: "Mermaid", value: "mermaid" },
+ { label: "Nix", value: "nix" },
+ { label: "Notion Formula", value: "notion" },
+ { label: "Objective-C", value: "objectivec" },
+ { label: "OCaml", value: "ocaml" },
+ { label: "Pascal", value: "pascal" },
+ { label: "Perl", value: "perl" },
+ { label: "PHP", value: "php" },
+ { label: "PowerShell", value: "powershell" },
+ { label: "Prolog", value: "prolog" },
+ { label: "Protocol Buffers", value: "protobuf" },
+ { label: "PureScript", value: "purescript" },
+ { label: "Python", value: "python" },
+ { label: "R", value: "r" },
+ { label: "Racket", value: "racket" },
+ { label: "Reason", value: "reasonml" },
+ { label: "Ruby", value: "ruby" },
+ { label: "Rust", value: "rust" },
+ { label: "Sass", value: "scss" },
+ { label: "Scala", value: "scala" },
+ { label: "Scheme", value: "scheme" },
+ { label: "SCSS", value: "scss" },
+ { label: "Shell", value: "shell" },
+ { label: "Smalltalk", value: "smalltalk" },
+ { label: "Solidity", value: "solidity" },
+ { label: "SQL", value: "sql" },
+ { label: "Swift", value: "swift" },
+ { label: "TOML", value: "toml" },
+ { label: "TypeScript", value: "typescript" },
+ { label: "VB.Net", value: "vbnet" },
+ { label: "Verilog", value: "verilog" },
+ { label: "VHDL", value: "vhdl" },
+ { label: "Visual Basic", value: "vbnet" },
+ { label: "WebAssembly", value: "wasm" },
+ { label: "XML", value: "xml" },
+ { label: "YAML", value: "yaml" },
];
diff --git a/surfsense_web/components/ui/code-node.tsx b/surfsense_web/components/ui/code-node.tsx
index 5295b9c83..d68cbd4ec 100644
--- a/surfsense_web/components/ui/code-node.tsx
+++ b/surfsense_web/components/ui/code-node.tsx
@@ -1,19 +1,19 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { PlateLeafProps } from 'platejs/react';
+import type { PlateLeafProps } from "platejs/react";
-import { PlateLeaf } from 'platejs/react';
+import { PlateLeaf } from "platejs/react";
export function CodeLeaf(props: PlateLeafProps) {
- return (
-
- {props.children}
-
- );
+ return (
+
+ {props.children}
+
+ );
}
diff --git a/surfsense_web/components/ui/dropdown-menu.tsx b/surfsense_web/components/ui/dropdown-menu.tsx
index 015dc5f5d..723a9466e 100644
--- a/surfsense_web/components/ui/dropdown-menu.tsx
+++ b/surfsense_web/components/ui/dropdown-menu.tsx
@@ -1,257 +1,228 @@
-"use client"
+"use client";
-import * as React from "react"
-import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
-import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
+import * as React from "react";
+import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
+import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
-function DropdownMenu({
- ...props
-}: React.ComponentProps) {
- return
+function DropdownMenu({ ...props }: React.ComponentProps) {
+ return ;
}
function DropdownMenuPortal({
- ...props
+ ...props
}: React.ComponentProps) {
- return (
-
- )
+ return ;
}
function DropdownMenuTrigger({
- ...props
+ ...props
}: React.ComponentProps) {
- return (
-
- )
+ return ;
}
function DropdownMenuContent({
- className,
- sideOffset = 4,
- ...props
+ className,
+ sideOffset = 4,
+ ...props
}: React.ComponentProps) {
- return (
-
-
-
- )
+ return (
+
+
+
+ );
}
-function DropdownMenuGroup({
- ...props
-}: React.ComponentProps) {
- return (
-
- )
+function DropdownMenuGroup({ ...props }: React.ComponentProps) {
+ return ;
}
function DropdownMenuItem({
- className,
- inset,
- variant = "default",
- ...props
+ className,
+ inset,
+ variant = "default",
+ ...props
}: React.ComponentProps & {
- inset?: boolean
- variant?: "default" | "destructive"
+ inset?: boolean;
+ variant?: "default" | "destructive";
}) {
- return (
-
- )
+ return (
+
+ );
}
function DropdownMenuCheckboxItem({
- className,
- children,
- checked,
- ...props
+ className,
+ children,
+ checked,
+ ...props
}: React.ComponentProps) {
- return (
-
-
-
-
-
-
- {children}
-
- )
+ return (
+
+
+
+
+
+
+ {children}
+
+ );
}
function DropdownMenuRadioGroup({
- ...props
+ ...props
}: React.ComponentProps) {
- return (
-
- )
+ return ;
}
function DropdownMenuRadioItem({
- className,
- children,
- ...props
+ className,
+ children,
+ ...props
}: React.ComponentProps) {
- return (
-
-
-
-
-
-
- {children}
-
- )
+ return (
+
+
+
+
+
+
+ {children}
+
+ );
}
function DropdownMenuLabel({
- className,
- inset,
- ...props
+ className,
+ inset,
+ ...props
}: React.ComponentProps & {
- inset?: boolean
+ inset?: boolean;
}) {
- return (
-
- )
+ return (
+
+ );
}
function DropdownMenuSeparator({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
- )
+ return (
+
+ );
}
-function DropdownMenuShortcut({
- className,
- ...props
-}: React.ComponentProps<"span">) {
- return (
-
- )
+function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
+ return (
+
+ );
}
-function DropdownMenuSub({
- ...props
-}: React.ComponentProps) {
- return
+function DropdownMenuSub({ ...props }: React.ComponentProps) {
+ return ;
}
function DropdownMenuSubTrigger({
- className,
- inset,
- children,
- ...props
+ className,
+ inset,
+ children,
+ ...props
}: React.ComponentProps & {
- inset?: boolean
+ inset?: boolean;
}) {
- return (
-
- {children}
-
-
- )
+ return (
+
+ {children}
+
+
+ );
}
function DropdownMenuSubContent({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
- )
+ return (
+
+ );
}
export {
- DropdownMenu,
- DropdownMenuPortal,
- DropdownMenuTrigger,
- DropdownMenuContent,
- DropdownMenuGroup,
- DropdownMenuLabel,
- DropdownMenuItem,
- DropdownMenuCheckboxItem,
- DropdownMenuRadioGroup,
- DropdownMenuRadioItem,
- DropdownMenuSeparator,
- DropdownMenuShortcut,
- DropdownMenuSub,
- DropdownMenuSubTrigger,
- DropdownMenuSubContent,
-}
+ DropdownMenu,
+ DropdownMenuPortal,
+ DropdownMenuTrigger,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuLabel,
+ DropdownMenuItem,
+ DropdownMenuCheckboxItem,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubTrigger,
+ DropdownMenuSubContent,
+};
diff --git a/surfsense_web/components/ui/editor.tsx b/surfsense_web/components/ui/editor.tsx
index 185327023..718704f3a 100644
--- a/surfsense_web/components/ui/editor.tsx
+++ b/surfsense_web/components/ui/editor.tsx
@@ -1,132 +1,124 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { VariantProps } from 'class-variance-authority';
-import type { PlateContentProps, PlateViewProps } from 'platejs/react';
+import type { VariantProps } from "class-variance-authority";
+import type { PlateContentProps, PlateViewProps } from "platejs/react";
-import { cva } from 'class-variance-authority';
-import { PlateContainer, PlateContent, PlateView } from 'platejs/react';
+import { cva } from "class-variance-authority";
+import { PlateContainer, PlateContent, PlateView } from "platejs/react";
-import { cn } from '@/lib/utils';
+import { cn } from "@/lib/utils";
const editorContainerVariants = cva(
- 'relative w-full cursor-text select-text overflow-y-auto caret-primary selection:bg-brand/25 focus-visible:outline-none [&_.slate-selection-area]:z-50 [&_.slate-selection-area]:border [&_.slate-selection-area]:border-brand/25 [&_.slate-selection-area]:bg-brand/15',
- {
- defaultVariants: {
- variant: 'default',
- },
- variants: {
- variant: {
- comment: cn(
- 'flex flex-wrap justify-between gap-1 px-1 py-0.5 text-sm',
- 'rounded-md border-[1.5px] border-transparent bg-transparent',
- 'has-[[data-slate-editor]:focus]:border-brand/50 has-[[data-slate-editor]:focus]:ring-2 has-[[data-slate-editor]:focus]:ring-brand/30',
- 'has-aria-disabled:border-input has-aria-disabled:bg-muted'
- ),
- default: 'h-full',
- demo: 'h-[650px]',
- select: cn(
- 'group rounded-md border border-input ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2',
- 'has-data-readonly:w-fit has-data-readonly:cursor-default has-data-readonly:border-transparent has-data-readonly:focus-within:[box-shadow:none]'
- ),
- },
- },
- }
+ "relative w-full cursor-text select-text overflow-y-auto caret-primary selection:bg-brand/25 focus-visible:outline-none [&_.slate-selection-area]:z-50 [&_.slate-selection-area]:border [&_.slate-selection-area]:border-brand/25 [&_.slate-selection-area]:bg-brand/15",
+ {
+ defaultVariants: {
+ variant: "default",
+ },
+ variants: {
+ variant: {
+ comment: cn(
+ "flex flex-wrap justify-between gap-1 px-1 py-0.5 text-sm",
+ "rounded-md border-[1.5px] border-transparent bg-transparent",
+ "has-[[data-slate-editor]:focus]:border-brand/50 has-[[data-slate-editor]:focus]:ring-2 has-[[data-slate-editor]:focus]:ring-brand/30",
+ "has-aria-disabled:border-input has-aria-disabled:bg-muted"
+ ),
+ default: "h-full",
+ demo: "h-[650px]",
+ select: cn(
+ "group rounded-md border border-input ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
+ "has-data-readonly:w-fit has-data-readonly:cursor-default has-data-readonly:border-transparent has-data-readonly:focus-within:[box-shadow:none]"
+ ),
+ },
+ },
+ }
);
export function EditorContainer({
- className,
- variant,
- ...props
-}: React.ComponentProps<'div'> & VariantProps) {
- return (
-
- );
+ className,
+ variant,
+ ...props
+}: React.ComponentProps<"div"> & VariantProps) {
+ return (
+
+ );
}
const editorVariants = cva(
- cn(
- 'group/editor',
- 'relative w-full cursor-text select-text overflow-x-hidden whitespace-pre-wrap break-words',
- 'rounded-md ring-offset-background focus-visible:outline-none',
- '**:data-slate-placeholder:!top-1/2 **:data-slate-placeholder:-translate-y-1/2 placeholder:text-muted-foreground/80 **:data-slate-placeholder:text-muted-foreground/80 **:data-slate-placeholder:opacity-100!',
- '[&_strong]:font-bold'
- ),
- {
- defaultVariants: {
- variant: 'default',
- },
- variants: {
- disabled: {
- true: 'cursor-not-allowed opacity-50',
- },
- focused: {
- true: 'ring-2 ring-ring ring-offset-2',
- },
- variant: {
- ai: 'w-full px-0 text-base md:text-sm',
- aiChat:
- 'max-h-[min(70vh,320px)] w-full overflow-y-auto px-3 py-2 text-base md:text-sm',
- comment: cn('rounded-none border-none bg-transparent text-sm'),
- default:
- 'size-full px-6 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
- demo: 'size-full px-6 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
- fullWidth: 'size-full px-6 pt-4 pb-72 text-base sm:px-24',
- none: '',
- select: 'px-3 py-2 text-base data-readonly:w-fit',
- },
- },
- }
+ cn(
+ "group/editor",
+ "relative w-full cursor-text select-text overflow-x-hidden whitespace-pre-wrap break-words",
+ "rounded-md ring-offset-background focus-visible:outline-none",
+ "**:data-slate-placeholder:!top-1/2 **:data-slate-placeholder:-translate-y-1/2 placeholder:text-muted-foreground/80 **:data-slate-placeholder:text-muted-foreground/80 **:data-slate-placeholder:opacity-100!",
+ "[&_strong]:font-bold"
+ ),
+ {
+ defaultVariants: {
+ variant: "default",
+ },
+ variants: {
+ disabled: {
+ true: "cursor-not-allowed opacity-50",
+ },
+ focused: {
+ true: "ring-2 ring-ring ring-offset-2",
+ },
+ variant: {
+ ai: "w-full px-0 text-base md:text-sm",
+ aiChat: "max-h-[min(70vh,320px)] w-full overflow-y-auto px-3 py-2 text-base md:text-sm",
+ comment: cn("rounded-none border-none bg-transparent text-sm"),
+ default: "size-full px-6 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]",
+ demo: "size-full px-6 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]",
+ fullWidth: "size-full px-6 pt-4 pb-72 text-base sm:px-24",
+ none: "",
+ select: "px-3 py-2 text-base data-readonly:w-fit",
+ },
+ },
+ }
);
-export type EditorProps = PlateContentProps &
- VariantProps;
+export type EditorProps = PlateContentProps & VariantProps;
export const Editor = ({
- className,
- disabled,
- focused,
- variant,
- ref,
- ...props
+ className,
+ disabled,
+ focused,
+ variant,
+ ref,
+ ...props
}: EditorProps & { ref?: React.RefObject }) => (
-
+
);
-Editor.displayName = 'Editor';
+Editor.displayName = "Editor";
export function EditorView({
- className,
- variant,
- ...props
+ className,
+ variant,
+ ...props
}: PlateViewProps & VariantProps) {
- return (
-
- );
+ return ;
}
-EditorView.displayName = 'EditorView';
+EditorView.displayName = "EditorView";
diff --git a/surfsense_web/components/ui/equation-node.tsx b/surfsense_web/components/ui/equation-node.tsx
index 66b3bb3d7..34c3ac329 100644
--- a/surfsense_web/components/ui/equation-node.tsx
+++ b/surfsense_web/components/ui/equation-node.tsx
@@ -1,183 +1,176 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { TEquationElement } from 'platejs';
+import type { TEquationElement } from "platejs";
-import {
- useEquationElement,
- useEquationInput,
-} from '@platejs/math/react';
-import { RadicalIcon } from 'lucide-react';
-import { type PlateElementProps, PlateElement, useSelected } from 'platejs/react';
+import { useEquationElement, useEquationInput } from "@platejs/math/react";
+import { RadicalIcon } from "lucide-react";
+import { type PlateElementProps, PlateElement, useSelected } from "platejs/react";
-import { cn } from '@/lib/utils';
+import { cn } from "@/lib/utils";
-export function EquationElement({
- children,
- ...props
-}: PlateElementProps) {
- const element = props.element;
- const selected = useSelected();
- const katexRef = React.useRef(null);
- const [isEditing, setIsEditing] = React.useState(false);
+export function EquationElement({ children, ...props }: PlateElementProps) {
+ const element = props.element;
+ const selected = useSelected();
+ const katexRef = React.useRef(null);
+ const [isEditing, setIsEditing] = React.useState(false);
- useEquationElement({
- element,
- katexRef,
- options: {
- displayMode: true,
- throwOnError: false,
- },
- });
+ useEquationElement({
+ element,
+ katexRef,
+ options: {
+ displayMode: true,
+ throwOnError: false,
+ },
+ });
- const { props: inputProps, ref: inputRef, onDismiss, onSubmit } = useEquationInput({
- isInline: false,
- open: isEditing,
- onClose: () => setIsEditing(false),
- });
+ const {
+ props: inputProps,
+ ref: inputRef,
+ onDismiss,
+ onSubmit,
+ } = useEquationInput({
+ isInline: false,
+ open: isEditing,
+ onClose: () => setIsEditing(false),
+ });
- return (
-
- setIsEditing(true)}
- >
- {element.texExpression ? (
-
- ) : (
-
-
- Add an equation
-
- )}
-
+ return (
+
+ setIsEditing(true)}
+ >
+ {element.texExpression ? (
+
+ ) : (
+
+
+ Add an equation
+
+ )}
+
- {isEditing && (
-
-
-
-
-
-
-
- )}
+ {isEditing && (
+
+
+
+
+
+
+
+ )}
- {children}
-
- );
+ {children}
+
+ );
}
-export function InlineEquationElement({
- children,
- ...props
-}: PlateElementProps) {
- const element = props.element;
- const selected = useSelected();
- const katexRef = React.useRef(null);
- const [isEditing, setIsEditing] = React.useState(false);
+export function InlineEquationElement({ children, ...props }: PlateElementProps) {
+ const element = props.element;
+ const selected = useSelected();
+ const katexRef = React.useRef(null);
+ const [isEditing, setIsEditing] = React.useState(false);
- useEquationElement({
- element,
- katexRef,
- options: {
- displayMode: false,
- throwOnError: false,
- },
- });
+ useEquationElement({
+ element,
+ katexRef,
+ options: {
+ displayMode: false,
+ throwOnError: false,
+ },
+ });
- const { props: inputProps, ref: inputRef, onDismiss, onSubmit } = useEquationInput({
- isInline: true,
- open: isEditing,
- onClose: () => setIsEditing(false),
- });
+ const {
+ props: inputProps,
+ ref: inputRef,
+ onDismiss,
+ onSubmit,
+ } = useEquationInput({
+ isInline: true,
+ open: isEditing,
+ onClose: () => setIsEditing(false),
+ });
- return (
-
- setIsEditing(true)}
- >
- {element.texExpression ? (
-
- ) : (
-
-
-
- )}
-
+ return (
+
+ setIsEditing(true)}
+ >
+ {element.texExpression ? (
+
+ ) : (
+
+
+
+ )}
+
- {isEditing && (
-
-
-
-
-
-
-
- )}
+ {isEditing && (
+
+
+
+
+
+
+
+ )}
- {children}
-
- );
+ {children}
+
+ );
}
-
diff --git a/surfsense_web/components/ui/fixed-toolbar-buttons.tsx b/surfsense_web/components/ui/fixed-toolbar-buttons.tsx
index 83322cfcc..e5f200bc2 100644
--- a/surfsense_web/components/ui/fixed-toolbar-buttons.tsx
+++ b/surfsense_web/components/ui/fixed-toolbar-buttons.tsx
@@ -1,138 +1,125 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
import {
- BoldIcon,
- Code2Icon,
- HighlighterIcon,
- ItalicIcon,
- RedoIcon,
- SaveIcon,
- StrikethroughIcon,
- UnderlineIcon,
- UndoIcon,
-} from 'lucide-react';
-import { KEYS } from 'platejs';
-import { useEditorReadOnly, useEditorRef } from 'platejs/react';
+ BoldIcon,
+ Code2Icon,
+ HighlighterIcon,
+ ItalicIcon,
+ RedoIcon,
+ SaveIcon,
+ StrikethroughIcon,
+ UnderlineIcon,
+ UndoIcon,
+} from "lucide-react";
+import { KEYS } from "platejs";
+import { useEditorReadOnly, useEditorRef } from "platejs/react";
-import { useEditorSave } from '@/components/editor/editor-save-context';
-import { Spinner } from '@/components/ui/spinner';
+import { useEditorSave } from "@/components/editor/editor-save-context";
+import { Spinner } from "@/components/ui/spinner";
-import { InsertToolbarButton } from './insert-toolbar-button';
-import { LinkToolbarButton } from './link-toolbar-button';
-import { MarkToolbarButton } from './mark-toolbar-button';
-import { ModeToolbarButton } from './mode-toolbar-button';
-import { ToolbarButton, ToolbarGroup } from './toolbar';
-import { TurnIntoToolbarButton } from './turn-into-toolbar-button';
+import { InsertToolbarButton } from "./insert-toolbar-button";
+import { LinkToolbarButton } from "./link-toolbar-button";
+import { MarkToolbarButton } from "./mark-toolbar-button";
+import { ModeToolbarButton } from "./mode-toolbar-button";
+import { ToolbarButton, ToolbarGroup } from "./toolbar";
+import { TurnIntoToolbarButton } from "./turn-into-toolbar-button";
export function FixedToolbarButtons() {
- const readOnly = useEditorReadOnly();
- const editor = useEditorRef();
- const { onSave, hasUnsavedChanges, isSaving, canToggleMode } = useEditorSave();
+ const readOnly = useEditorReadOnly();
+ const editor = useEditorRef();
+ const { onSave, hasUnsavedChanges, isSaving, canToggleMode } = useEditorSave();
- return (
-
- {/* Scrollable editing buttons */}
-
- {!readOnly && (
- <>
-
- {
- editor.undo();
- editor.tf.focus();
- }}
- >
-
-
+ return (
+
+ {/* Scrollable editing buttons */}
+
+ {!readOnly && (
+ <>
+
+ {
+ editor.undo();
+ editor.tf.focus();
+ }}
+ >
+
+
- {
- editor.redo();
- editor.tf.focus();
- }}
- >
-
-
-
+ {
+ editor.redo();
+ editor.tf.focus();
+ }}
+ >
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
-
-
- >
- )}
-
+
+
+
+ >
+ )}
+
- {/* Fixed right-side buttons (Save + Mode) */}
-
- {/* Save button — only in edit mode with unsaved changes */}
- {!readOnly && onSave && hasUnsavedChanges && (
-
-
- {isSaving ? (
-
- ) : (
-
- )}
-
-
- )}
+ {/* Fixed right-side buttons (Save + Mode) */}
+
+ {/* Save button — only in edit mode with unsaved changes */}
+ {!readOnly && onSave && hasUnsavedChanges && (
+
+
+ {isSaving ? : }
+
+
+ )}
- {/* Mode toggle */}
- {canToggleMode && (
-
-
-
- )}
-
-
- );
+ {/* Mode toggle */}
+ {canToggleMode && (
+
+
+
+ )}
+
+
+ );
}
diff --git a/surfsense_web/components/ui/fixed-toolbar.tsx b/surfsense_web/components/ui/fixed-toolbar.tsx
index 40d513587..ffe57bd65 100644
--- a/surfsense_web/components/ui/fixed-toolbar.tsx
+++ b/surfsense_web/components/ui/fixed-toolbar.tsx
@@ -1,26 +1,25 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import { cn } from '@/lib/utils';
+import { cn } from "@/lib/utils";
-import { Toolbar } from './toolbar';
+import { Toolbar } from "./toolbar";
export function FixedToolbar({
- children,
- className,
- ...props
+ children,
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
- {children}
-
- );
+ return (
+
+ {children}
+
+ );
}
-
diff --git a/surfsense_web/components/ui/floating-toolbar-buttons.tsx b/surfsense_web/components/ui/floating-toolbar-buttons.tsx
index 8baed4e8f..2d8659065 100644
--- a/surfsense_web/components/ui/floating-toolbar-buttons.tsx
+++ b/surfsense_web/components/ui/floating-toolbar-buttons.tsx
@@ -1,55 +1,48 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import {
- BoldIcon,
- Code2Icon,
- ItalicIcon,
- StrikethroughIcon,
- UnderlineIcon,
-} from 'lucide-react';
-import { KEYS } from 'platejs';
-import { useEditorReadOnly } from 'platejs/react';
+import { BoldIcon, Code2Icon, ItalicIcon, StrikethroughIcon, UnderlineIcon } from "lucide-react";
+import { KEYS } from "platejs";
+import { useEditorReadOnly } from "platejs/react";
-import { LinkToolbarButton } from './link-toolbar-button';
-import { MarkToolbarButton } from './mark-toolbar-button';
-import { ToolbarGroup } from './toolbar';
-import { TurnIntoToolbarButton } from './turn-into-toolbar-button';
+import { LinkToolbarButton } from "./link-toolbar-button";
+import { MarkToolbarButton } from "./mark-toolbar-button";
+import { ToolbarGroup } from "./toolbar";
+import { TurnIntoToolbarButton } from "./turn-into-toolbar-button";
export function FloatingToolbarButtons() {
- const readOnly = useEditorReadOnly();
+ const readOnly = useEditorReadOnly();
- if (readOnly) return null;
+ if (readOnly) return null;
- return (
- <>
-
-
+ return (
+ <>
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
- >
- );
+
+
+ >
+ );
}
diff --git a/surfsense_web/components/ui/floating-toolbar.tsx b/surfsense_web/components/ui/floating-toolbar.tsx
index f3ae544f4..f788901d9 100644
--- a/surfsense_web/components/ui/floating-toolbar.tsx
+++ b/surfsense_web/components/ui/floating-toolbar.tsx
@@ -1,88 +1,79 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
import {
- type FloatingToolbarState,
- flip,
- offset,
- useFloatingToolbar,
- useFloatingToolbarState,
-} from '@platejs/floating';
-import { useComposedRef } from '@udecode/cn';
-import { KEYS } from 'platejs';
-import {
- useEditorId,
- useEventEditorValue,
- usePluginOption,
-} from 'platejs/react';
+ type FloatingToolbarState,
+ flip,
+ offset,
+ useFloatingToolbar,
+ useFloatingToolbarState,
+} from "@platejs/floating";
+import { useComposedRef } from "@udecode/cn";
+import { KEYS } from "platejs";
+import { useEditorId, useEventEditorValue, usePluginOption } from "platejs/react";
-import { cn } from '@/lib/utils';
-import { useIsMobile } from '@/hooks/use-mobile';
+import { cn } from "@/lib/utils";
+import { useIsMobile } from "@/hooks/use-mobile";
-import { Toolbar } from './toolbar';
+import { Toolbar } from "./toolbar";
export function FloatingToolbar({
- children,
- className,
- state,
- ...props
+ children,
+ className,
+ state,
+ ...props
}: React.ComponentProps & {
- state?: FloatingToolbarState;
+ state?: FloatingToolbarState;
}) {
- const editorId = useEditorId();
- const focusedEditorId = useEventEditorValue('focus');
- const isFloatingLinkOpen = !!usePluginOption({ key: KEYS.link }, 'mode');
- const isMobile = useIsMobile();
+ const editorId = useEditorId();
+ const focusedEditorId = useEventEditorValue("focus");
+ const isFloatingLinkOpen = !!usePluginOption({ key: KEYS.link }, "mode");
+ const isMobile = useIsMobile();
- const floatingToolbarState = useFloatingToolbarState({
- editorId,
- focusedEditorId,
- hideToolbar: isFloatingLinkOpen,
- ...state,
- floatingOptions: {
- middleware: [
- offset(12),
- flip({
- fallbackPlacements: [
- 'top-start',
- 'top-end',
- 'bottom-start',
- 'bottom-end',
- ],
- padding: 12,
- }),
- ],
- placement: 'top',
- ...state?.floatingOptions,
- },
- });
+ const floatingToolbarState = useFloatingToolbarState({
+ editorId,
+ focusedEditorId,
+ hideToolbar: isFloatingLinkOpen,
+ ...state,
+ floatingOptions: {
+ middleware: [
+ offset(12),
+ flip({
+ fallbackPlacements: ["top-start", "top-end", "bottom-start", "bottom-end"],
+ padding: 12,
+ }),
+ ],
+ placement: "top",
+ ...state?.floatingOptions,
+ },
+ });
- const {
- clickOutsideRef,
- hidden,
- props: rootProps,
- ref: floatingRef,
- } = useFloatingToolbar(floatingToolbarState);
+ const {
+ clickOutsideRef,
+ hidden,
+ props: rootProps,
+ ref: floatingRef,
+ } = useFloatingToolbar(floatingToolbarState);
- const ref = useComposedRef(props.ref, floatingRef);
+ const ref = useComposedRef(props.ref, floatingRef);
- if (hidden || isMobile) return null;
+ if (hidden || isMobile) return null;
- return (
-
-
- {children}
-
-
- );
+ return (
+
+
+ {children}
+
+
+ );
}
diff --git a/surfsense_web/components/ui/heading-node.tsx b/surfsense_web/components/ui/heading-node.tsx
index 3713b6aed..7e5c92dee 100644
--- a/surfsense_web/components/ui/heading-node.tsx
+++ b/surfsense_web/components/ui/heading-node.tsx
@@ -1,60 +1,56 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { PlateElementProps } from 'platejs/react';
+import type { PlateElementProps } from "platejs/react";
-import { type VariantProps, cva } from 'class-variance-authority';
-import { PlateElement } from 'platejs/react';
+import { type VariantProps, cva } from "class-variance-authority";
+import { PlateElement } from "platejs/react";
-const headingVariants = cva('relative mb-1', {
- variants: {
- variant: {
- h1: 'mt-[1.6em] pb-1 font-bold font-heading text-4xl',
- h2: 'mt-[1.4em] pb-px font-heading font-semibold text-2xl tracking-tight',
- h3: 'mt-[1em] pb-px font-heading font-semibold text-xl tracking-tight',
- h4: 'mt-[0.75em] font-heading font-semibold text-lg tracking-tight',
- h5: 'mt-[0.75em] font-semibold text-lg tracking-tight',
- h6: 'mt-[0.75em] font-semibold text-base tracking-tight',
- },
- },
+const headingVariants = cva("relative mb-1", {
+ variants: {
+ variant: {
+ h1: "mt-[1.6em] pb-1 font-bold font-heading text-4xl",
+ h2: "mt-[1.4em] pb-px font-heading font-semibold text-2xl tracking-tight",
+ h3: "mt-[1em] pb-px font-heading font-semibold text-xl tracking-tight",
+ h4: "mt-[0.75em] font-heading font-semibold text-lg tracking-tight",
+ h5: "mt-[0.75em] font-semibold text-lg tracking-tight",
+ h6: "mt-[0.75em] font-semibold text-base tracking-tight",
+ },
+ },
});
export function HeadingElement({
- variant = 'h1',
- ...props
+ variant = "h1",
+ ...props
}: PlateElementProps & VariantProps) {
- return (
-
- {props.children}
-
- );
+ return (
+
+ {props.children}
+
+ );
}
export function H1Element(props: PlateElementProps) {
- return ;
+ return ;
}
export function H2Element(props: PlateElementProps) {
- return ;
+ return ;
}
export function H3Element(props: PlateElementProps) {
- return ;
+ return ;
}
export function H4Element(props: PlateElementProps) {
- return ;
+ return ;
}
export function H5Element(props: PlateElementProps) {
- return ;
+ return ;
}
export function H6Element(props: PlateElementProps) {
- return ;
+ return ;
}
diff --git a/surfsense_web/components/ui/highlight-node.tsx b/surfsense_web/components/ui/highlight-node.tsx
index b292dceba..f032129ff 100644
--- a/surfsense_web/components/ui/highlight-node.tsx
+++ b/surfsense_web/components/ui/highlight-node.tsx
@@ -1,15 +1,15 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { PlateLeafProps } from 'platejs/react';
+import type { PlateLeafProps } from "platejs/react";
-import { PlateLeaf } from 'platejs/react';
+import { PlateLeaf } from "platejs/react";
export function HighlightLeaf(props: PlateLeafProps) {
- return (
-
- {props.children}
-
- );
+ return (
+
+ {props.children}
+
+ );
}
diff --git a/surfsense_web/components/ui/hr-node.tsx b/surfsense_web/components/ui/hr-node.tsx
index 3e86f1ab9..949e08c7c 100644
--- a/surfsense_web/components/ui/hr-node.tsx
+++ b/surfsense_web/components/ui/hr-node.tsx
@@ -1,35 +1,30 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { PlateElementProps } from 'platejs/react';
+import type { PlateElementProps } from "platejs/react";
-import {
- PlateElement,
- useFocused,
- useReadOnly,
- useSelected,
-} from 'platejs/react';
+import { PlateElement, useFocused, useReadOnly, useSelected } from "platejs/react";
-import { cn } from '@/lib/utils';
+import { cn } from "@/lib/utils";
export function HrElement(props: PlateElementProps) {
- const readOnly = useReadOnly();
- const selected = useSelected();
- const focused = useFocused();
+ const readOnly = useReadOnly();
+ const selected = useSelected();
+ const focused = useFocused();
- return (
-
-
-
-
- {props.children}
-
- );
+ return (
+
+
+
+
+ {props.children}
+
+ );
}
diff --git a/surfsense_web/components/ui/inline-combobox.tsx b/surfsense_web/components/ui/inline-combobox.tsx
index 2fe8c8cad..a254631ba 100644
--- a/surfsense_web/components/ui/inline-combobox.tsx
+++ b/surfsense_web/components/ui/inline-combobox.tsx
@@ -1,450 +1,406 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { Point, TElement } from 'platejs';
+import type { Point, TElement } from "platejs";
import {
- type ComboboxItemProps,
- Combobox,
- ComboboxGroup,
- ComboboxGroupLabel,
- ComboboxItem,
- ComboboxPopover,
- ComboboxProvider,
- ComboboxRow,
- Portal,
- useComboboxContext,
- useComboboxStore,
-} from '@ariakit/react';
-import { filterWords } from '@platejs/combobox';
+ type ComboboxItemProps,
+ Combobox,
+ ComboboxGroup,
+ ComboboxGroupLabel,
+ ComboboxItem,
+ ComboboxPopover,
+ ComboboxProvider,
+ ComboboxRow,
+ Portal,
+ useComboboxContext,
+ useComboboxStore,
+} from "@ariakit/react";
+import { filterWords } from "@platejs/combobox";
import {
- type UseComboboxInputResult,
- useComboboxInput,
- useHTMLInputCursorState,
-} from '@platejs/combobox/react';
-import { cva } from 'class-variance-authority';
-import { useComposedRef, useEditorRef } from 'platejs/react';
+ type UseComboboxInputResult,
+ useComboboxInput,
+ useHTMLInputCursorState,
+} from "@platejs/combobox/react";
+import { cva } from "class-variance-authority";
+import { useComposedRef, useEditorRef } from "platejs/react";
-import { cn } from '@/lib/utils';
+import { cn } from "@/lib/utils";
function useRequiredComboboxContext() {
- const context = useComboboxContext();
+ const context = useComboboxContext();
- if (!context) {
- throw new Error(
- 'InlineCombobox compound components must be rendered within InlineCombobox'
- );
- }
+ if (!context) {
+ throw new Error("InlineCombobox compound components must be rendered within InlineCombobox");
+ }
- return context;
+ return context;
}
type FilterFn = (
- item: { value: string; group?: string; keywords?: string[]; label?: string },
- search: string
+ item: { value: string; group?: string; keywords?: string[]; label?: string },
+ search: string
) => boolean;
type InlineComboboxContextValue = {
- filter: FilterFn | false;
- inputProps: UseComboboxInputResult['props'];
- inputRef: React.RefObject;
- removeInput: UseComboboxInputResult['removeInput'];
- showTrigger: boolean;
- trigger: string;
- setHasEmpty: (hasEmpty: boolean) => void;
+ filter: FilterFn | false;
+ inputProps: UseComboboxInputResult["props"];
+ inputRef: React.RefObject;
+ removeInput: UseComboboxInputResult["removeInput"];
+ showTrigger: boolean;
+ trigger: string;
+ setHasEmpty: (hasEmpty: boolean) => void;
};
const InlineComboboxContext = React.createContext(
- null as unknown as InlineComboboxContextValue
+ null as unknown as InlineComboboxContextValue
);
-const defaultFilter: FilterFn = (
- { group, keywords = [], label, value },
- search
-) => {
- const uniqueTerms = new Set(
- [value, ...keywords, group, label].filter(Boolean)
- );
+const defaultFilter: FilterFn = ({ group, keywords = [], label, value }, search) => {
+ const uniqueTerms = new Set([value, ...keywords, group, label].filter(Boolean));
- return Array.from(uniqueTerms).some((keyword) =>
- filterWords(keyword as string, search)
- );
+ return Array.from(uniqueTerms).some((keyword) => filterWords(keyword as string, search));
};
type InlineComboboxProps = {
- children: React.ReactNode;
- element: TElement;
- trigger: string;
- filter?: FilterFn | false;
- hideWhenNoValue?: boolean;
- showTrigger?: boolean;
- value?: string;
- setValue?: (value: string) => void;
+ children: React.ReactNode;
+ element: TElement;
+ trigger: string;
+ filter?: FilterFn | false;
+ hideWhenNoValue?: boolean;
+ showTrigger?: boolean;
+ value?: string;
+ setValue?: (value: string) => void;
};
const InlineCombobox = ({
- children,
- element,
- filter = defaultFilter,
- hideWhenNoValue = false,
- setValue: setValueProp,
- showTrigger = true,
- trigger,
- value: valueProp,
+ children,
+ element,
+ filter = defaultFilter,
+ hideWhenNoValue = false,
+ setValue: setValueProp,
+ showTrigger = true,
+ trigger,
+ value: valueProp,
}: InlineComboboxProps) => {
- const editor = useEditorRef();
- const inputRef = React.useRef(null);
- const cursorState = useHTMLInputCursorState(inputRef);
+ const editor = useEditorRef();
+ const inputRef = React.useRef(null);
+ const cursorState = useHTMLInputCursorState(inputRef);
- const [valueState, setValueState] = React.useState('');
- const hasValueProp = valueProp !== undefined;
- const value = hasValueProp ? valueProp : valueState;
+ const [valueState, setValueState] = React.useState("");
+ const hasValueProp = valueProp !== undefined;
+ const value = hasValueProp ? valueProp : valueState;
- // Check if current user is the creator of this element (for Yjs collaboration)
- const isCreator = React.useMemo(() => {
- const elementUserId = (element as Record).userId;
- const currentUserId = editor.meta.userId;
+ // Check if current user is the creator of this element (for Yjs collaboration)
+ const isCreator = React.useMemo(() => {
+ const elementUserId = (element as Record).userId;
+ const currentUserId = editor.meta.userId;
- // If no userId (backwards compatibility or non-Yjs), allow
- if (!elementUserId) return true;
+ // If no userId (backwards compatibility or non-Yjs), allow
+ if (!elementUserId) return true;
- return elementUserId === currentUserId;
- }, [editor.meta.userId, element]);
+ return elementUserId === currentUserId;
+ }, [editor.meta.userId, element]);
- const setValue = React.useCallback(
- (newValue: string) => {
- setValueProp?.(newValue);
+ const setValue = React.useCallback(
+ (newValue: string) => {
+ setValueProp?.(newValue);
- if (!hasValueProp) {
- setValueState(newValue);
- }
- },
- [setValueProp, hasValueProp]
- );
+ if (!hasValueProp) {
+ setValueState(newValue);
+ }
+ },
+ [setValueProp, hasValueProp]
+ );
- /**
- * Track the point just before the input element so we know where to
- * insertText if the combobox closes due to a selection change.
- */
- const insertPoint = React.useRef(null);
+ /**
+ * Track the point just before the input element so we know where to
+ * insertText if the combobox closes due to a selection change.
+ */
+ const insertPoint = React.useRef(null);
- React.useEffect(() => {
- const path = editor.api.findPath(element);
+ React.useEffect(() => {
+ const path = editor.api.findPath(element);
- if (!path) return;
+ if (!path) return;
- const point = editor.api.before(path);
+ const point = editor.api.before(path);
- if (!point) return;
+ if (!point) return;
- const pointRef = editor.api.pointRef(point);
- insertPoint.current = pointRef.current;
+ const pointRef = editor.api.pointRef(point);
+ insertPoint.current = pointRef.current;
- return () => {
- pointRef.unref();
- };
- }, [editor, element]);
+ return () => {
+ pointRef.unref();
+ };
+ }, [editor, element]);
- const { props: inputProps, removeInput } = useComboboxInput({
- cancelInputOnBlur: true,
- cursorState,
- autoFocus: isCreator,
- ref: inputRef,
- onCancelInput: (cause) => {
- if (cause !== 'backspace') {
- editor.tf.insertText(trigger + value, {
- at: insertPoint?.current ?? undefined,
- });
- }
- if (cause === 'arrowLeft' || cause === 'arrowRight') {
- editor.tf.move({
- distance: 1,
- reverse: cause === 'arrowLeft',
- });
- }
- },
- });
+ const { props: inputProps, removeInput } = useComboboxInput({
+ cancelInputOnBlur: true,
+ cursorState,
+ autoFocus: isCreator,
+ ref: inputRef,
+ onCancelInput: (cause) => {
+ if (cause !== "backspace") {
+ editor.tf.insertText(trigger + value, {
+ at: insertPoint?.current ?? undefined,
+ });
+ }
+ if (cause === "arrowLeft" || cause === "arrowRight") {
+ editor.tf.move({
+ distance: 1,
+ reverse: cause === "arrowLeft",
+ });
+ }
+ },
+ });
- const [hasEmpty, setHasEmpty] = React.useState(false);
+ const [hasEmpty, setHasEmpty] = React.useState(false);
- const contextValue: InlineComboboxContextValue = React.useMemo(
- () => ({
- filter,
- inputProps,
- inputRef,
- removeInput,
- setHasEmpty,
- showTrigger,
- trigger,
- }),
- [
- trigger,
- showTrigger,
- filter,
- inputProps,
- removeInput,
- ]
- );
+ const contextValue: InlineComboboxContextValue = React.useMemo(
+ () => ({
+ filter,
+ inputProps,
+ inputRef,
+ removeInput,
+ setHasEmpty,
+ showTrigger,
+ trigger,
+ }),
+ [trigger, showTrigger, filter, inputProps, removeInput]
+ );
- const store = useComboboxStore({
- // open: ,
- setValue: (newValue) => React.startTransition(() => setValue(newValue)),
- });
+ const store = useComboboxStore({
+ // open: ,
+ setValue: (newValue) => React.startTransition(() => setValue(newValue)),
+ });
- const items = store.useState('items');
+ const items = store.useState("items");
- /**
- * If there is no active ID and the list of items changes, select the first
- * item.
- */
- React.useEffect(() => {
- if (items.length === 0) return;
+ /**
+ * If there is no active ID and the list of items changes, select the first
+ * item.
+ */
+ React.useEffect(() => {
+ if (items.length === 0) return;
- if (!store.getState().activeId) {
- store.setActiveId(store.first());
- }
- }, [items, store]);
+ if (!store.getState().activeId) {
+ store.setActiveId(store.first());
+ }
+ }, [items, store]);
- return (
-
- 0 || hasEmpty) &&
- (!hideWhenNoValue || value.length > 0)
- }
- store={store}
- >
-
- {children}
-
-
-
- );
+ return (
+
+ 0 || hasEmpty) && (!hideWhenNoValue || value.length > 0)}
+ store={store}
+ >
+
+ {children}
+
+
+
+ );
};
const InlineComboboxInput = ({
- className,
- ref: propRef,
- ...props
+ className,
+ ref: propRef,
+ ...props
}: React.HTMLAttributes & {
- ref?: React.RefObject;
+ ref?: React.RefObject;
}) => {
- const {
- inputProps,
- inputRef: contextRef,
- showTrigger,
- trigger,
- } = React.useContext(InlineComboboxContext);
+ const {
+ inputProps,
+ inputRef: contextRef,
+ showTrigger,
+ trigger,
+ } = React.useContext(InlineComboboxContext);
- const store = useRequiredComboboxContext();
- const value = store.useState('value');
+ const store = useRequiredComboboxContext();
+ const value = store.useState("value");
- const ref = useComposedRef(propRef, contextRef);
+ const ref = useComposedRef(propRef, contextRef);
- /**
- * To create an auto-resizing input, we render a visually hidden span
- * containing the input value and position the input element on top of it.
- * This works well for all cases except when input exceeds the width of the
- * container.
- */
+ /**
+ * To create an auto-resizing input, we render a visually hidden span
+ * containing the input value and position the input element on top of it.
+ * This works well for all cases except when input exceeds the width of the
+ * container.
+ */
- return (
- <>
- {showTrigger && trigger}
+ return (
+ <>
+ {showTrigger && trigger}
-
-
- {value || '\u200B'}
-
+
+
+ {value || "\u200B"}
+
-
-
- >
- );
+
+
+ >
+ );
};
-InlineComboboxInput.displayName = 'InlineComboboxInput';
+InlineComboboxInput.displayName = "InlineComboboxInput";
-const InlineComboboxContent: typeof ComboboxPopover = ({
- className,
- ...props
-}) => {
- // Portal prevents CSS from leaking into popover
- const store = useComboboxContext();
+const InlineComboboxContent: typeof ComboboxPopover = ({ className, ...props }) => {
+ // Portal prevents CSS from leaking into popover
+ const store = useComboboxContext();
- function handleKeyDown(event: React.KeyboardEvent) {
- if (!store) return;
+ function handleKeyDown(event: React.KeyboardEvent) {
+ if (!store) return;
- const state = store.getState();
- const { items, activeId } = state;
+ const state = store.getState();
+ const { items, activeId } = state;
- if (!items.length) return;
+ if (!items.length) return;
- const currentIndex = items.findIndex((item) => item.id === activeId);
+ const currentIndex = items.findIndex((item) => item.id === activeId);
- if (event.key === 'ArrowUp' && currentIndex <= 0) {
- event.preventDefault();
- store.setActiveId(store.last());
- } else if (event.key === 'ArrowDown' && currentIndex >= items.length - 1) {
- event.preventDefault();
- store.setActiveId(store.first());
- }
- }
+ if (event.key === "ArrowUp" && currentIndex <= 0) {
+ event.preventDefault();
+ store.setActiveId(store.last());
+ } else if (event.key === "ArrowDown" && currentIndex >= items.length - 1) {
+ event.preventDefault();
+ store.setActiveId(store.first());
+ }
+ }
- return (
-
-
-
- );
+ return (
+
+
+
+ );
};
const comboboxItemVariants = cva(
- 'relative mx-1 flex h-[28px] select-none items-center rounded-sm px-2 text-foreground text-sm outline-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
- {
- defaultVariants: {
- interactive: true,
- },
- variants: {
- interactive: {
- false: '',
- true: 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground dark:hover:bg-neutral-700 dark:data-[active-item=true]:bg-neutral-700',
- },
- },
- }
+ "relative mx-1 flex h-[28px] select-none items-center rounded-sm px-2 text-foreground text-sm outline-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ {
+ defaultVariants: {
+ interactive: true,
+ },
+ variants: {
+ interactive: {
+ false: "",
+ true: "cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground dark:hover:bg-neutral-700 dark:data-[active-item=true]:bg-neutral-700",
+ },
+ },
+ }
);
const InlineComboboxItem = ({
- className,
- focusEditor = true,
- group,
- keywords,
- label,
- onClick,
- ...props
+ className,
+ focusEditor = true,
+ group,
+ keywords,
+ label,
+ onClick,
+ ...props
}: {
- focusEditor?: boolean;
- group?: string;
- keywords?: string[];
- label?: string;
+ focusEditor?: boolean;
+ group?: string;
+ keywords?: string[];
+ label?: string;
} & ComboboxItemProps &
- Required>) => {
- const { value } = props;
+ Required>) => {
+ const { value } = props;
- const { filter, removeInput } = React.useContext(InlineComboboxContext);
+ const { filter, removeInput } = React.useContext(InlineComboboxContext);
- const store = useRequiredComboboxContext();
+ const store = useRequiredComboboxContext();
- // Always call hook unconditionally; only use value if filter is active
- const storeValue = store.useState('value');
- const search = filter ? storeValue : '';
+ // Always call hook unconditionally; only use value if filter is active
+ const storeValue = store.useState("value");
+ const search = filter ? storeValue : "";
- const visible = React.useMemo(
- () =>
- !filter || filter({ group, keywords, label, value }, search),
- [filter, group, keywords, label, value, search]
- );
+ const visible = React.useMemo(
+ () => !filter || filter({ group, keywords, label, value }, search),
+ [filter, group, keywords, label, value, search]
+ );
- if (!visible) return null;
+ if (!visible) return null;
- return (
- {
- removeInput(focusEditor);
- onClick?.(event);
- }}
- {...props}
- />
- );
+ return (
+ {
+ removeInput(focusEditor);
+ onClick?.(event);
+ }}
+ {...props}
+ />
+ );
};
-const InlineComboboxEmpty = ({
- children,
- className,
-}: React.HTMLAttributes) => {
- const { setHasEmpty } = React.useContext(InlineComboboxContext);
- const store = useRequiredComboboxContext();
- const items = store.useState('items');
+const InlineComboboxEmpty = ({ children, className }: React.HTMLAttributes) => {
+ const { setHasEmpty } = React.useContext(InlineComboboxContext);
+ const store = useRequiredComboboxContext();
+ const items = store.useState("items");
- React.useEffect(() => {
- setHasEmpty(true);
+ React.useEffect(() => {
+ setHasEmpty(true);
- return () => {
- setHasEmpty(false);
- };
- }, [setHasEmpty]);
+ return () => {
+ setHasEmpty(false);
+ };
+ }, [setHasEmpty]);
- if (items.length > 0) return null;
+ if (items.length > 0) return null;
- return (
-
- {children}
-
- );
+ return (
+ {children}
+ );
};
const InlineComboboxRow = ComboboxRow;
-function InlineComboboxGroup({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
+function InlineComboboxGroup({ className, ...props }: React.ComponentProps) {
+ return (
+
+ );
}
function InlineComboboxGroupLabel({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
- );
+ return (
+
+ );
}
export {
- InlineCombobox,
- InlineComboboxContent,
- InlineComboboxEmpty,
- InlineComboboxGroup,
- InlineComboboxGroupLabel,
- InlineComboboxInput,
- InlineComboboxItem,
- InlineComboboxRow,
+ InlineCombobox,
+ InlineComboboxContent,
+ InlineComboboxEmpty,
+ InlineComboboxGroup,
+ InlineComboboxGroupLabel,
+ InlineComboboxInput,
+ InlineComboboxItem,
+ InlineComboboxRow,
};
diff --git a/surfsense_web/components/ui/insert-toolbar-button.tsx b/surfsense_web/components/ui/insert-toolbar-button.tsx
index 666704c0c..437087bc7 100644
--- a/surfsense_web/components/ui/insert-toolbar-button.tsx
+++ b/surfsense_web/components/ui/insert-toolbar-button.tsx
@@ -1,229 +1,225 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
+import type { DropdownMenuProps } from "@radix-ui/react-dropdown-menu";
import {
- ChevronRightIcon,
- FileCodeIcon,
- Heading1Icon,
- Heading2Icon,
- Heading3Icon,
- InfoIcon,
- ListIcon,
- ListOrderedIcon,
- MinusIcon,
- PilcrowIcon,
- PlusIcon,
- QuoteIcon,
- RadicalIcon,
- SquareIcon,
- SubscriptIcon,
- SuperscriptIcon,
- TableIcon,
-} from 'lucide-react';
-import { KEYS } from 'platejs';
-import { type PlateEditor, useEditorRef } from 'platejs/react';
+ ChevronRightIcon,
+ FileCodeIcon,
+ Heading1Icon,
+ Heading2Icon,
+ Heading3Icon,
+ InfoIcon,
+ ListIcon,
+ ListOrderedIcon,
+ MinusIcon,
+ PilcrowIcon,
+ PlusIcon,
+ QuoteIcon,
+ RadicalIcon,
+ SquareIcon,
+ SubscriptIcon,
+ SuperscriptIcon,
+ TableIcon,
+} from "lucide-react";
+import { KEYS } from "platejs";
+import { type PlateEditor, useEditorRef } from "platejs/react";
import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from '@/components/ui/dropdown-menu';
-import {
- insertBlock,
- insertInlineElement,
-} from '@/components/editor/transforms';
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { insertBlock, insertInlineElement } from "@/components/editor/transforms";
-import { ToolbarButton, ToolbarMenuGroup } from './toolbar';
+import { ToolbarButton, ToolbarMenuGroup } from "./toolbar";
type Group = {
- group: string;
- items: Item[];
+ group: string;
+ items: Item[];
};
type Item = {
- icon: React.ReactNode;
- value: string;
- onSelect: (editor: PlateEditor, value: string) => void;
- focusEditor?: boolean;
- label?: string;
+ icon: React.ReactNode;
+ value: string;
+ onSelect: (editor: PlateEditor, value: string) => void;
+ focusEditor?: boolean;
+ label?: string;
};
const groups: Group[] = [
- {
- group: 'Basic blocks',
- items: [
- {
- icon: ,
- label: 'Paragraph',
- value: KEYS.p,
- },
- {
- icon: ,
- label: 'Heading 1',
- value: 'h1',
- },
- {
- icon: ,
- label: 'Heading 2',
- value: 'h2',
- },
- {
- icon: ,
- label: 'Heading 3',
- value: 'h3',
- },
- {
- icon: ,
- label: 'Table',
- value: KEYS.table,
- },
- {
- icon: ,
- label: 'Code block',
- value: KEYS.codeBlock,
- },
- {
- icon: ,
- label: 'Quote',
- value: KEYS.blockquote,
- },
- {
- icon: ,
- label: 'Divider',
- value: KEYS.hr,
- },
- ].map((item) => ({
- ...item,
- onSelect: (editor: PlateEditor, value: string) => {
- insertBlock(editor, value);
- },
- })),
- },
- {
- group: 'Lists',
- items: [
- {
- icon: ,
- label: 'Bulleted list',
- value: KEYS.ul,
- },
- {
- icon: ,
- label: 'Numbered list',
- value: KEYS.ol,
- },
- {
- icon: ,
- label: 'To-do list',
- value: KEYS.listTodo,
- },
- {
- icon: ,
- label: 'Toggle list',
- value: KEYS.toggle,
- },
- ].map((item) => ({
- ...item,
- onSelect: (editor: PlateEditor, value: string) => {
- insertBlock(editor, value);
- },
- })),
- },
- {
- group: 'Advanced',
- items: [
- {
- icon: ,
- label: 'Callout',
- value: KEYS.callout,
- },
- {
- focusEditor: false,
- icon: ,
- label: 'Equation',
- value: KEYS.equation,
- },
- ].map((item) => ({
- ...item,
- onSelect: (editor: PlateEditor, value: string) => {
- if (item.value === KEYS.equation) {
- insertInlineElement(editor, value);
- } else {
- insertBlock(editor, value);
- }
- },
- })),
- },
- {
- group: 'Marks',
- items: [
- {
- icon: ,
- label: 'Superscript',
- value: KEYS.sup,
- },
- {
- icon: ,
- label: 'Subscript',
- value: KEYS.sub,
- },
- ].map((item) => ({
- ...item,
- onSelect: (editor: PlateEditor, value: string) => {
- editor.tf.toggleMark(value, {
- remove: value === KEYS.sup ? KEYS.sub : KEYS.sup,
- });
- },
- })),
- },
+ {
+ group: "Basic blocks",
+ items: [
+ {
+ icon: ,
+ label: "Paragraph",
+ value: KEYS.p,
+ },
+ {
+ icon: ,
+ label: "Heading 1",
+ value: "h1",
+ },
+ {
+ icon: ,
+ label: "Heading 2",
+ value: "h2",
+ },
+ {
+ icon: ,
+ label: "Heading 3",
+ value: "h3",
+ },
+ {
+ icon: ,
+ label: "Table",
+ value: KEYS.table,
+ },
+ {
+ icon: ,
+ label: "Code block",
+ value: KEYS.codeBlock,
+ },
+ {
+ icon: ,
+ label: "Quote",
+ value: KEYS.blockquote,
+ },
+ {
+ icon: ,
+ label: "Divider",
+ value: KEYS.hr,
+ },
+ ].map((item) => ({
+ ...item,
+ onSelect: (editor: PlateEditor, value: string) => {
+ insertBlock(editor, value);
+ },
+ })),
+ },
+ {
+ group: "Lists",
+ items: [
+ {
+ icon: ,
+ label: "Bulleted list",
+ value: KEYS.ul,
+ },
+ {
+ icon: ,
+ label: "Numbered list",
+ value: KEYS.ol,
+ },
+ {
+ icon: ,
+ label: "To-do list",
+ value: KEYS.listTodo,
+ },
+ {
+ icon: ,
+ label: "Toggle list",
+ value: KEYS.toggle,
+ },
+ ].map((item) => ({
+ ...item,
+ onSelect: (editor: PlateEditor, value: string) => {
+ insertBlock(editor, value);
+ },
+ })),
+ },
+ {
+ group: "Advanced",
+ items: [
+ {
+ icon: ,
+ label: "Callout",
+ value: KEYS.callout,
+ },
+ {
+ focusEditor: false,
+ icon: ,
+ label: "Equation",
+ value: KEYS.equation,
+ },
+ ].map((item) => ({
+ ...item,
+ onSelect: (editor: PlateEditor, value: string) => {
+ if (item.value === KEYS.equation) {
+ insertInlineElement(editor, value);
+ } else {
+ insertBlock(editor, value);
+ }
+ },
+ })),
+ },
+ {
+ group: "Marks",
+ items: [
+ {
+ icon: ,
+ label: "Superscript",
+ value: KEYS.sup,
+ },
+ {
+ icon: ,
+ label: "Subscript",
+ value: KEYS.sub,
+ },
+ ].map((item) => ({
+ ...item,
+ onSelect: (editor: PlateEditor, value: string) => {
+ editor.tf.toggleMark(value, {
+ remove: value === KEYS.sup ? KEYS.sub : KEYS.sup,
+ });
+ },
+ })),
+ },
];
export function InsertToolbarButton(props: DropdownMenuProps) {
- const editor = useEditorRef();
- const [open, setOpen] = React.useState(false);
+ const editor = useEditorRef();
+ const [open, setOpen] = React.useState(false);
- return (
-
-
-
-
-
-
+ return (
+
+
+
+
+
+
-
- {groups.map(({ group, items }) => (
-
-
- {items.map(({ icon, label, value, onSelect, focusEditor }) => (
- {
- onSelect(editor, value);
- if (focusEditor !== false) {
- editor.tf.focus();
- }
- setOpen(false);
- }}
- className="group"
- >
-
- {icon}
- {label || value}
-
-
- ))}
-
-
- ))}
-
-
- );
+
+ {groups.map(({ group, items }) => (
+
+
+ {items.map(({ icon, label, value, onSelect, focusEditor }) => (
+ {
+ onSelect(editor, value);
+ if (focusEditor !== false) {
+ editor.tf.focus();
+ }
+ setOpen(false);
+ }}
+ className="group"
+ >
+
+ {icon}
+ {label || value}
+
+
+ ))}
+
+
+ ))}
+
+
+ );
}
-
diff --git a/surfsense_web/components/ui/link-node.tsx b/surfsense_web/components/ui/link-node.tsx
index 82e916ed0..0692939dd 100644
--- a/surfsense_web/components/ui/link-node.tsx
+++ b/surfsense_web/components/ui/link-node.tsx
@@ -1,32 +1,32 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { TLinkElement } from 'platejs';
-import type { PlateElementProps } from 'platejs/react';
+import type { TLinkElement } from "platejs";
+import type { PlateElementProps } from "platejs/react";
-import { getLinkAttributes } from '@platejs/link';
-import { PlateElement } from 'platejs/react';
+import { getLinkAttributes } from "@platejs/link";
+import { PlateElement } from "platejs/react";
-import { cn } from '@/lib/utils';
+import { cn } from "@/lib/utils";
export function LinkElement(props: PlateElementProps) {
- return (
- {
- e.stopPropagation();
- },
- }}
- >
- {props.children}
-
- );
+ return (
+ {
+ e.stopPropagation();
+ },
+ }}
+ >
+ {props.children}
+
+ );
}
diff --git a/surfsense_web/components/ui/link-toolbar-button.tsx b/surfsense_web/components/ui/link-toolbar-button.tsx
index a63d9d4eb..3589de772 100644
--- a/surfsense_web/components/ui/link-toolbar-button.tsx
+++ b/surfsense_web/components/ui/link-toolbar-button.tsx
@@ -1,24 +1,19 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import {
- useLinkToolbarButton,
- useLinkToolbarButtonState,
-} from '@platejs/link/react';
-import { Link } from 'lucide-react';
+import { useLinkToolbarButton, useLinkToolbarButtonState } from "@platejs/link/react";
+import { Link } from "lucide-react";
-import { ToolbarButton } from './toolbar';
+import { ToolbarButton } from "./toolbar";
-export function LinkToolbarButton(
- props: React.ComponentProps
-) {
- const state = useLinkToolbarButtonState();
- const { props: buttonProps } = useLinkToolbarButton(state);
+export function LinkToolbarButton(props: React.ComponentProps) {
+ const state = useLinkToolbarButtonState();
+ const { props: buttonProps } = useLinkToolbarButton(state);
- return (
-
-
-
- );
+ return (
+
+
+
+ );
}
diff --git a/surfsense_web/components/ui/link-toolbar.tsx b/surfsense_web/components/ui/link-toolbar.tsx
index ba9611fa4..b73247387 100644
--- a/surfsense_web/components/ui/link-toolbar.tsx
+++ b/surfsense_web/components/ui/link-toolbar.tsx
@@ -1,208 +1,196 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { TLinkElement } from 'platejs';
+import type { TLinkElement } from "platejs";
+import { type UseVirtualFloatingOptions, flip, offset } from "@platejs/floating";
+import { getLinkAttributes } from "@platejs/link";
import {
- type UseVirtualFloatingOptions,
- flip,
- offset,
-} from '@platejs/floating';
-import { getLinkAttributes } from '@platejs/link';
+ type LinkFloatingToolbarState,
+ FloatingLinkUrlInput,
+ useFloatingLinkEdit,
+ useFloatingLinkEditState,
+ useFloatingLinkInsert,
+ useFloatingLinkInsertState,
+} from "@platejs/link/react";
+import { cva } from "class-variance-authority";
+import { ExternalLink, Link, Text, Unlink } from "lucide-react";
+import { KEYS } from "platejs";
import {
- type LinkFloatingToolbarState,
- FloatingLinkUrlInput,
- useFloatingLinkEdit,
- useFloatingLinkEditState,
- useFloatingLinkInsert,
- useFloatingLinkInsertState,
-} from '@platejs/link/react';
-import { cva } from 'class-variance-authority';
-import { ExternalLink, Link, Text, Unlink } from 'lucide-react';
-import { KEYS } from 'platejs';
-import {
- useEditorRef,
- useEditorSelection,
- useFormInputProps,
- usePluginOption,
-} from 'platejs/react';
+ useEditorRef,
+ useEditorSelection,
+ useFormInputProps,
+ usePluginOption,
+} from "platejs/react";
-import { buttonVariants } from '@/components/ui/button';
-import { Separator } from '@/components/ui/separator';
+import { buttonVariants } from "@/components/ui/button";
+import { Separator } from "@/components/ui/separator";
const popoverVariants = cva(
- 'z-50 w-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-hidden'
+ "z-50 w-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-hidden"
);
const inputVariants = cva(
- 'flex h-[28px] w-full rounded-md border-none bg-transparent px-1.5 py-1 text-base placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-transparent md:text-sm'
+ "flex h-[28px] w-full rounded-md border-none bg-transparent px-1.5 py-1 text-base placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-transparent md:text-sm"
);
-export function LinkFloatingToolbar({
- state,
-}: {
- state?: LinkFloatingToolbarState;
-}) {
- const activeCommentId = usePluginOption({ key: KEYS.comment }, 'activeId');
- const activeSuggestionId = usePluginOption(
- { key: KEYS.suggestion },
- 'activeId'
- );
+export function LinkFloatingToolbar({ state }: { state?: LinkFloatingToolbarState }) {
+ const activeCommentId = usePluginOption({ key: KEYS.comment }, "activeId");
+ const activeSuggestionId = usePluginOption({ key: KEYS.suggestion }, "activeId");
- const floatingOptions: UseVirtualFloatingOptions = React.useMemo(
- () => ({
- middleware: [
- offset(8),
- flip({
- fallbackPlacements: ['bottom-end', 'top-start', 'top-end'],
- padding: 12,
- }),
- ],
- placement:
- activeSuggestionId || activeCommentId ? 'top-start' : 'bottom-start',
- }),
- [activeCommentId, activeSuggestionId]
- );
+ const floatingOptions: UseVirtualFloatingOptions = React.useMemo(
+ () => ({
+ middleware: [
+ offset(8),
+ flip({
+ fallbackPlacements: ["bottom-end", "top-start", "top-end"],
+ padding: 12,
+ }),
+ ],
+ placement: activeSuggestionId || activeCommentId ? "top-start" : "bottom-start",
+ }),
+ [activeCommentId, activeSuggestionId]
+ );
- const insertState = useFloatingLinkInsertState({
- ...state,
- floatingOptions: {
- ...floatingOptions,
- ...state?.floatingOptions,
- },
- });
- const {
- hidden,
- props: insertProps,
- ref: insertRef,
- textInputProps,
- } = useFloatingLinkInsert(insertState);
+ const insertState = useFloatingLinkInsertState({
+ ...state,
+ floatingOptions: {
+ ...floatingOptions,
+ ...state?.floatingOptions,
+ },
+ });
+ const {
+ hidden,
+ props: insertProps,
+ ref: insertRef,
+ textInputProps,
+ } = useFloatingLinkInsert(insertState);
- const editState = useFloatingLinkEditState({
- ...state,
- floatingOptions: {
- ...floatingOptions,
- ...state?.floatingOptions,
- },
- });
- const {
- editButtonProps,
- props: editProps,
- ref: editRef,
- unlinkButtonProps,
- } = useFloatingLinkEdit(editState);
- const inputProps = useFormInputProps({
- preventDefaultOnEnterKeydown: true,
- });
+ const editState = useFloatingLinkEditState({
+ ...state,
+ floatingOptions: {
+ ...floatingOptions,
+ ...state?.floatingOptions,
+ },
+ });
+ const {
+ editButtonProps,
+ props: editProps,
+ ref: editRef,
+ unlinkButtonProps,
+ } = useFloatingLinkEdit(editState);
+ const inputProps = useFormInputProps({
+ preventDefaultOnEnterKeydown: true,
+ });
- if (hidden) return null;
+ if (hidden) return null;
- const input = (
-
-
-
-
-
+ const input = (
+
- );
+
+
+
+
+
+ );
- const editContent = editState.isEditing ? (
- input
- ) : (
-
-
+ const editContent = editState.isEditing ? (
+ input
+ ) : (
+
+
-
+
-
+
-
+
-
-
- );
+
+
+ );
- return (
- <>
-
- {input}
-
+ return (
+ <>
+
+ {input}
+
-
- {editContent}
-
- >
- );
+
+ {editContent}
+
+ >
+ );
}
function LinkOpenButton() {
- const editor = useEditorRef();
- const selection = useEditorSelection();
+ const editor = useEditorRef();
+ const selection = useEditorSelection();
- const attributes = React.useMemo(
- () => {
- const entry = editor.api.node({
- match: { type: editor.getType(KEYS.link) },
- });
- if (!entry) {
- return {};
- }
- const [element] = entry;
- return getLinkAttributes(editor, element);
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [editor, selection]
- );
+ const attributes = React.useMemo(
+ () => {
+ const entry = editor.api.node({
+ match: { type: editor.getType(KEYS.link) },
+ });
+ if (!entry) {
+ return {};
+ }
+ const [element] = entry;
+ return getLinkAttributes(editor, element);
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [editor, selection]
+ );
- return (
- {
- e.stopPropagation();
- }}
- aria-label="Open link in a new tab"
- target="_blank"
- >
-
-
- );
+ return (
+ {
+ e.stopPropagation();
+ }}
+ aria-label="Open link in a new tab"
+ target="_blank"
+ >
+
+
+ );
}
diff --git a/surfsense_web/components/ui/mark-toolbar-button.tsx b/surfsense_web/components/ui/mark-toolbar-button.tsx
index 4534ab8b9..b616698a8 100644
--- a/surfsense_web/components/ui/mark-toolbar-button.tsx
+++ b/surfsense_web/components/ui/mark-toolbar-button.tsx
@@ -1,29 +1,29 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import { useMarkToolbarButton, useMarkToolbarButtonState } from 'platejs/react';
+import { useMarkToolbarButton, useMarkToolbarButtonState } from "platejs/react";
-import { ToolbarButton } from './toolbar';
+import { ToolbarButton } from "./toolbar";
export function MarkToolbarButton({
- clear,
- nodeType,
- ...props
+ clear,
+ nodeType,
+ ...props
}: React.ComponentProps & {
- nodeType: string;
- clear?: string[] | string;
+ nodeType: string;
+ clear?: string[] | string;
}) {
- const state = useMarkToolbarButtonState({ clear, nodeType });
- const { props: buttonProps } = useMarkToolbarButton(state);
+ const state = useMarkToolbarButtonState({ clear, nodeType });
+ const { props: buttonProps } = useMarkToolbarButton(state);
- return (
- {
- e.preventDefault();
- }}
- />
- );
+ return (
+ {
+ e.preventDefault();
+ }}
+ />
+ );
}
diff --git a/surfsense_web/components/ui/mode-toolbar-button.tsx b/surfsense_web/components/ui/mode-toolbar-button.tsx
index fab0b591e..37231991f 100644
--- a/surfsense_web/components/ui/mode-toolbar-button.tsx
+++ b/surfsense_web/components/ui/mode-toolbar-button.tsx
@@ -1,19 +1,19 @@
-'use client';
+"use client";
-import { BookOpenIcon, PenLineIcon } from 'lucide-react';
-import { usePlateState } from 'platejs/react';
+import { BookOpenIcon, PenLineIcon } from "lucide-react";
+import { usePlateState } from "platejs/react";
-import { ToolbarButton } from './toolbar';
+import { ToolbarButton } from "./toolbar";
export function ModeToolbarButton() {
- const [readOnly, setReadOnly] = usePlateState('readOnly');
+ const [readOnly, setReadOnly] = usePlateState("readOnly");
- return (
- setReadOnly(!readOnly)}
- >
- {readOnly ? : }
-
- );
+ return (
+ setReadOnly(!readOnly)}
+ >
+ {readOnly ? : }
+
+ );
}
diff --git a/surfsense_web/components/ui/paragraph-node.tsx b/surfsense_web/components/ui/paragraph-node.tsx
index e4004049f..bef198dec 100644
--- a/surfsense_web/components/ui/paragraph-node.tsx
+++ b/surfsense_web/components/ui/paragraph-node.tsx
@@ -1,17 +1,17 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { PlateElementProps } from 'platejs/react';
+import type { PlateElementProps } from "platejs/react";
-import { PlateElement } from 'platejs/react';
+import { PlateElement } from "platejs/react";
-import { cn } from '@/lib/utils';
+import { cn } from "@/lib/utils";
export function ParagraphElement(props: PlateElementProps) {
- return (
-
- {props.children}
-
- );
+ return (
+
+ {props.children}
+
+ );
}
diff --git a/surfsense_web/components/ui/resize-handle.tsx b/surfsense_web/components/ui/resize-handle.tsx
index 73f42a1b5..05390a8da 100644
--- a/surfsense_web/components/ui/resize-handle.tsx
+++ b/surfsense_web/components/ui/resize-handle.tsx
@@ -1,89 +1,79 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { VariantProps } from 'class-variance-authority';
+import type { VariantProps } from "class-variance-authority";
import {
- type ResizeHandle as ResizeHandlePrimitive,
- Resizable as ResizablePrimitive,
- useResizeHandle,
- useResizeHandleState,
-} from '@platejs/resizable';
-import { cva } from 'class-variance-authority';
+ type ResizeHandle as ResizeHandlePrimitive,
+ Resizable as ResizablePrimitive,
+ useResizeHandle,
+ useResizeHandleState,
+} from "@platejs/resizable";
+import { cva } from "class-variance-authority";
-import { cn } from '@/lib/utils';
+import { cn } from "@/lib/utils";
export const mediaResizeHandleVariants = cva(
- cn(
- 'top-0 flex w-6 select-none flex-col justify-center',
- "after:flex after:h-16 after:w-[3px] after:rounded-[6px] after:bg-ring after:opacity-0 after:content-['_'] group-hover:after:opacity-100"
- ),
- {
- variants: {
- direction: {
- left: '-left-3 -ml-3 pl-3',
- right: '-right-3 -mr-3 items-end pr-3',
- },
- },
- }
+ cn(
+ "top-0 flex w-6 select-none flex-col justify-center",
+ "after:flex after:h-16 after:w-[3px] after:rounded-[6px] after:bg-ring after:opacity-0 after:content-['_'] group-hover:after:opacity-100"
+ ),
+ {
+ variants: {
+ direction: {
+ left: "-left-3 -ml-3 pl-3",
+ right: "-right-3 -mr-3 items-end pr-3",
+ },
+ },
+ }
);
-const resizeHandleVariants = cva('absolute z-40', {
- variants: {
- direction: {
- bottom: 'w-full cursor-row-resize',
- left: 'h-full cursor-col-resize',
- right: 'h-full cursor-col-resize',
- top: 'w-full cursor-row-resize',
- },
- },
+const resizeHandleVariants = cva("absolute z-40", {
+ variants: {
+ direction: {
+ bottom: "w-full cursor-row-resize",
+ left: "h-full cursor-col-resize",
+ right: "h-full cursor-col-resize",
+ top: "w-full cursor-row-resize",
+ },
+ },
});
export function ResizeHandle({
- className,
- options,
- ...props
-}: React.ComponentProps &
- VariantProps) {
- const state = useResizeHandleState(options ?? {});
- const resizeHandle = useResizeHandle(state);
+ className,
+ options,
+ ...props
+}: React.ComponentProps & VariantProps) {
+ const state = useResizeHandleState(options ?? {});
+ const resizeHandle = useResizeHandle(state);
- if (state.readOnly) return null;
+ if (state.readOnly) return null;
- return (
-
- );
+ return (
+
+ );
}
-const resizableVariants = cva('', {
- variants: {
- align: {
- center: 'mx-auto',
- left: 'mr-auto',
- right: 'ml-auto',
- },
- },
+const resizableVariants = cva("", {
+ variants: {
+ align: {
+ center: "mx-auto",
+ left: "mr-auto",
+ right: "ml-auto",
+ },
+ },
});
export function Resizable({
- align,
- className,
- ...props
-}: React.ComponentProps &
- VariantProps) {
- return (
-
- );
+ align,
+ className,
+ ...props
+}: React.ComponentProps & VariantProps) {
+ return ;
}
diff --git a/surfsense_web/components/ui/separator.tsx b/surfsense_web/components/ui/separator.tsx
index 4c24b2a88..98f0a73ac 100644
--- a/surfsense_web/components/ui/separator.tsx
+++ b/surfsense_web/components/ui/separator.tsx
@@ -1,28 +1,28 @@
-"use client"
+"use client";
-import * as React from "react"
-import { Separator as SeparatorPrimitive } from "radix-ui"
+import * as React from "react";
+import { Separator as SeparatorPrimitive } from "radix-ui";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
function Separator({
- className,
- orientation = "horizontal",
- decorative = true,
- ...props
+ className,
+ orientation = "horizontal",
+ decorative = true,
+ ...props
}: React.ComponentProps) {
- return (
-
- )
+ return (
+
+ );
}
-export { Separator }
+export { Separator };
diff --git a/surfsense_web/components/ui/slash-node.tsx b/surfsense_web/components/ui/slash-node.tsx
index 09df86e86..07badcd16 100644
--- a/surfsense_web/components/ui/slash-node.tsx
+++ b/surfsense_web/components/ui/slash-node.tsx
@@ -1,223 +1,217 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { PlateElementProps } from 'platejs/react';
+import type { PlateElementProps } from "platejs/react";
-import { SlashInputPlugin } from '@platejs/slash-command/react';
+import { SlashInputPlugin } from "@platejs/slash-command/react";
import {
- ChevronRightIcon,
- Code2Icon,
- FileCodeIcon,
- Heading1Icon,
- Heading2Icon,
- Heading3Icon,
- InfoIcon,
- ListIcon,
- ListOrderedIcon,
- MinusIcon,
- PilcrowIcon,
- QuoteIcon,
- RadicalIcon,
- SquareIcon,
- TableIcon,
-} from 'lucide-react';
-import { KEYS } from 'platejs';
-import { PlateElement, useEditorRef } from 'platejs/react';
+ ChevronRightIcon,
+ Code2Icon,
+ FileCodeIcon,
+ Heading1Icon,
+ Heading2Icon,
+ Heading3Icon,
+ InfoIcon,
+ ListIcon,
+ ListOrderedIcon,
+ MinusIcon,
+ PilcrowIcon,
+ QuoteIcon,
+ RadicalIcon,
+ SquareIcon,
+ TableIcon,
+} from "lucide-react";
+import { KEYS } from "platejs";
+import { PlateElement, useEditorRef } from "platejs/react";
import {
- InlineCombobox,
- InlineComboboxContent,
- InlineComboboxEmpty,
- InlineComboboxGroup,
- InlineComboboxGroupLabel,
- InlineComboboxInput,
- InlineComboboxItem,
-} from '@/components/ui/inline-combobox';
-import { insertBlock, insertInlineElement } from '@/components/editor/transforms';
+ InlineCombobox,
+ InlineComboboxContent,
+ InlineComboboxEmpty,
+ InlineComboboxGroup,
+ InlineComboboxGroupLabel,
+ InlineComboboxInput,
+ InlineComboboxItem,
+} from "@/components/ui/inline-combobox";
+import { insertBlock, insertInlineElement } from "@/components/editor/transforms";
interface SlashCommandItem {
- icon: React.ReactNode;
- keywords: string[];
- label: string;
- value: string;
- onSelect: (editor: any) => void;
+ icon: React.ReactNode;
+ keywords: string[];
+ label: string;
+ value: string;
+ onSelect: (editor: any) => void;
}
const slashCommandGroups: { heading: string; items: SlashCommandItem[] }[] = [
- {
- heading: 'Basic Blocks',
- items: [
- {
- icon: ,
- keywords: ['paragraph', 'text', 'plain'],
- label: 'Text',
- value: 'text',
- onSelect: (editor) => insertBlock(editor, KEYS.p),
- },
- {
- icon: ,
- keywords: ['title', 'h1', 'heading'],
- label: 'Heading 1',
- value: 'heading1',
- onSelect: (editor) => insertBlock(editor, 'h1'),
- },
- {
- icon: ,
- keywords: ['subtitle', 'h2', 'heading'],
- label: 'Heading 2',
- value: 'heading2',
- onSelect: (editor) => insertBlock(editor, 'h2'),
- },
- {
- icon: ,
- keywords: ['subtitle', 'h3', 'heading'],
- label: 'Heading 3',
- value: 'heading3',
- onSelect: (editor) => insertBlock(editor, 'h3'),
- },
- {
- icon: ,
- keywords: ['citation', 'blockquote'],
- label: 'Quote',
- value: 'quote',
- onSelect: (editor) => insertBlock(editor, KEYS.blockquote),
- },
- {
- icon: ,
- keywords: ['divider', 'separator', 'line'],
- label: 'Divider',
- value: 'divider',
- onSelect: (editor) => insertBlock(editor, KEYS.hr),
- },
- ],
- },
- {
- heading: 'Lists',
- items: [
- {
- icon: ,
- keywords: ['unordered', 'ul', 'bullet'],
- label: 'Bulleted list',
- value: 'bulleted-list',
- onSelect: (editor) => insertBlock(editor, KEYS.ul),
- },
- {
- icon: ,
- keywords: ['ordered', 'ol', 'numbered'],
- label: 'Numbered list',
- value: 'numbered-list',
- onSelect: (editor) => insertBlock(editor, KEYS.ol),
- },
- {
- icon: ,
- keywords: ['checklist', 'task', 'checkbox', 'todo'],
- label: 'To-do list',
- value: 'todo-list',
- onSelect: (editor) => insertBlock(editor, KEYS.listTodo),
- },
- ],
- },
- {
- heading: 'Advanced',
- items: [
- {
- icon: ,
- keywords: ['table', 'grid'],
- label: 'Table',
- value: 'table',
- onSelect: (editor) => insertBlock(editor, KEYS.table),
- },
- {
- icon: ,
- keywords: ['code', 'codeblock', 'snippet'],
- label: 'Code block',
- value: 'code-block',
- onSelect: (editor) => insertBlock(editor, KEYS.codeBlock),
- },
- {
- icon: ,
- keywords: ['callout', 'note', 'info', 'warning', 'tip'],
- label: 'Callout',
- value: 'callout',
- onSelect: (editor) => insertBlock(editor, KEYS.callout),
- },
- {
- icon: ,
- keywords: ['toggle', 'collapsible', 'expand'],
- label: 'Toggle',
- value: 'toggle',
- onSelect: (editor) => insertBlock(editor, KEYS.toggle),
- },
- {
- icon: ,
- keywords: ['equation', 'math', 'formula', 'latex'],
- label: 'Equation',
- value: 'equation',
- onSelect: (editor) => insertInlineElement(editor, KEYS.equation),
- },
- ],
- },
- {
- heading: 'Inline',
- items: [
- {
- icon: ,
- keywords: ['link', 'url', 'href'],
- label: 'Link',
- value: 'link',
- onSelect: (editor) => insertInlineElement(editor, KEYS.link),
- },
- ],
- },
+ {
+ heading: "Basic Blocks",
+ items: [
+ {
+ icon: ,
+ keywords: ["paragraph", "text", "plain"],
+ label: "Text",
+ value: "text",
+ onSelect: (editor) => insertBlock(editor, KEYS.p),
+ },
+ {
+ icon: ,
+ keywords: ["title", "h1", "heading"],
+ label: "Heading 1",
+ value: "heading1",
+ onSelect: (editor) => insertBlock(editor, "h1"),
+ },
+ {
+ icon: ,
+ keywords: ["subtitle", "h2", "heading"],
+ label: "Heading 2",
+ value: "heading2",
+ onSelect: (editor) => insertBlock(editor, "h2"),
+ },
+ {
+ icon: ,
+ keywords: ["subtitle", "h3", "heading"],
+ label: "Heading 3",
+ value: "heading3",
+ onSelect: (editor) => insertBlock(editor, "h3"),
+ },
+ {
+ icon: ,
+ keywords: ["citation", "blockquote"],
+ label: "Quote",
+ value: "quote",
+ onSelect: (editor) => insertBlock(editor, KEYS.blockquote),
+ },
+ {
+ icon: ,
+ keywords: ["divider", "separator", "line"],
+ label: "Divider",
+ value: "divider",
+ onSelect: (editor) => insertBlock(editor, KEYS.hr),
+ },
+ ],
+ },
+ {
+ heading: "Lists",
+ items: [
+ {
+ icon: ,
+ keywords: ["unordered", "ul", "bullet"],
+ label: "Bulleted list",
+ value: "bulleted-list",
+ onSelect: (editor) => insertBlock(editor, KEYS.ul),
+ },
+ {
+ icon: ,
+ keywords: ["ordered", "ol", "numbered"],
+ label: "Numbered list",
+ value: "numbered-list",
+ onSelect: (editor) => insertBlock(editor, KEYS.ol),
+ },
+ {
+ icon: ,
+ keywords: ["checklist", "task", "checkbox", "todo"],
+ label: "To-do list",
+ value: "todo-list",
+ onSelect: (editor) => insertBlock(editor, KEYS.listTodo),
+ },
+ ],
+ },
+ {
+ heading: "Advanced",
+ items: [
+ {
+ icon: ,
+ keywords: ["table", "grid"],
+ label: "Table",
+ value: "table",
+ onSelect: (editor) => insertBlock(editor, KEYS.table),
+ },
+ {
+ icon: ,
+ keywords: ["code", "codeblock", "snippet"],
+ label: "Code block",
+ value: "code-block",
+ onSelect: (editor) => insertBlock(editor, KEYS.codeBlock),
+ },
+ {
+ icon: ,
+ keywords: ["callout", "note", "info", "warning", "tip"],
+ label: "Callout",
+ value: "callout",
+ onSelect: (editor) => insertBlock(editor, KEYS.callout),
+ },
+ {
+ icon: ,
+ keywords: ["toggle", "collapsible", "expand"],
+ label: "Toggle",
+ value: "toggle",
+ onSelect: (editor) => insertBlock(editor, KEYS.toggle),
+ },
+ {
+ icon: ,
+ keywords: ["equation", "math", "formula", "latex"],
+ label: "Equation",
+ value: "equation",
+ onSelect: (editor) => insertInlineElement(editor, KEYS.equation),
+ },
+ ],
+ },
+ {
+ heading: "Inline",
+ items: [
+ {
+ icon: ,
+ keywords: ["link", "url", "href"],
+ label: "Link",
+ value: "link",
+ onSelect: (editor) => insertInlineElement(editor, KEYS.link),
+ },
+ ],
+ },
];
-export function SlashInputElement({
- children,
- ...props
-}: PlateElementProps) {
- const editor = useEditorRef();
+export function SlashInputElement({ children, ...props }: PlateElementProps) {
+ const editor = useEditorRef();
- return (
-
-
-
+ return (
+
+
+
-
- No results found.
+
+ No results found.
- {slashCommandGroups.map(({ heading, items }) => (
-
- {heading}
+ {slashCommandGroups.map(({ heading, items }) => (
+
+ {heading}
- {items.map(({ icon, keywords, label, value, onSelect }) => (
- {
- onSelect(editor);
- editor.tf.focus();
- }}
- >
-
- {icon}
-
- {label}
-
- ))}
-
- ))}
-
-
+ {items.map(({ icon, keywords, label, value, onSelect }) => (
+ {
+ onSelect(editor);
+ editor.tf.focus();
+ }}
+ >
+
+ {icon}
+
+ {label}
+
+ ))}
+
+ ))}
+
+
- {children}
-
- );
+ {children}
+
+ );
}
diff --git a/surfsense_web/components/ui/table-icons.tsx b/surfsense_web/components/ui/table-icons.tsx
index e08578912..1e793d0dd 100644
--- a/surfsense_web/components/ui/table-icons.tsx
+++ b/surfsense_web/components/ui/table-icons.tsx
@@ -1,862 +1,685 @@
-'use client';
+"use client";
-import type { LucideProps } from 'lucide-react';
+import type { LucideProps } from "lucide-react";
export function BorderAllIcon(props: LucideProps) {
- return (
-
- );
+ return (
+
+ );
}
export function BorderBottomIcon(props: LucideProps) {
- return (
-
- );
+ return (
+
+ );
}
export function BorderLeftIcon(props: LucideProps) {
- return (
-
- );
+ return (
+
+ );
}
export function BorderNoneIcon(props: LucideProps) {
- return (
-
- );
+ return (
+
+ );
}
export function BorderRightIcon(props: LucideProps) {
- return (
-
- );
+ return (
+
+ );
}
export function BorderTopIcon(props: LucideProps) {
- return (
-
- );
+ return (
+
+ );
}
diff --git a/surfsense_web/components/ui/table-node.tsx b/surfsense_web/components/ui/table-node.tsx
index 2341a23a7..3c29dd8e1 100644
--- a/surfsense_web/components/ui/table-node.tsx
+++ b/surfsense_web/components/ui/table-node.tsx
@@ -1,484 +1,439 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import { useDraggable, useDropLine } from '@platejs/dnd';
+import { useDraggable, useDropLine } from "@platejs/dnd";
+import { BlockSelectionPlugin, useBlockSelected } from "@platejs/selection/react";
import {
- BlockSelectionPlugin,
- useBlockSelected,
-} from '@platejs/selection/react';
+ TablePlugin,
+ TableProvider,
+ useTableCellElement,
+ useTableCellElementResizable,
+ useTableElement,
+ useTableMergeState,
+} from "@platejs/table/react";
+import { PopoverAnchor } from "@radix-ui/react-popover";
+import { cva } from "class-variance-authority";
import {
- TablePlugin,
- TableProvider,
- useTableCellElement,
- useTableCellElementResizable,
- useTableElement,
- useTableMergeState,
-} from '@platejs/table/react';
-import { PopoverAnchor } from '@radix-ui/react-popover';
-import { cva } from 'class-variance-authority';
+ ArrowDown,
+ ArrowLeft,
+ ArrowRight,
+ ArrowUp,
+ CombineIcon,
+ GripVertical,
+ SquareSplitHorizontalIcon,
+ Trash2Icon,
+ XIcon,
+} from "lucide-react";
import {
- ArrowDown,
- ArrowLeft,
- ArrowRight,
- ArrowUp,
- CombineIcon,
- GripVertical,
- SquareSplitHorizontalIcon,
- Trash2Icon,
- XIcon,
-} from 'lucide-react';
+ type TElement,
+ type TTableCellElement,
+ type TTableElement,
+ type TTableRowElement,
+ KEYS,
+ PathApi,
+} from "platejs";
import {
- type TElement,
- type TTableCellElement,
- type TTableElement,
- type TTableRowElement,
- KEYS,
- PathApi,
-} from 'platejs';
-import {
- type PlateElementProps,
- PlateElement,
- useComposedRef,
- useEditorPlugin,
- useEditorRef,
- useEditorSelector,
- useElement,
- useFocusedLast,
- usePluginOption,
- useReadOnly,
- useRemoveNodeButton,
- useSelected,
- withHOC,
-} from 'platejs/react';
-import { useElementSelector } from 'platejs/react';
+ type PlateElementProps,
+ PlateElement,
+ useComposedRef,
+ useEditorPlugin,
+ useEditorRef,
+ useEditorSelector,
+ useElement,
+ useFocusedLast,
+ usePluginOption,
+ useReadOnly,
+ useRemoveNodeButton,
+ useSelected,
+ withHOC,
+} from "platejs/react";
+import { useElementSelector } from "platejs/react";
-import { Button } from '@/components/ui/button';
-import { Popover, PopoverContent } from '@/components/ui/popover';
-import { cn } from '@/lib/utils';
+import { Button } from "@/components/ui/button";
+import { Popover, PopoverContent } from "@/components/ui/popover";
+import { cn } from "@/lib/utils";
-import { blockSelectionVariants } from './block-selection';
-import { ResizeHandle } from './resize-handle';
-import {
- Toolbar,
- ToolbarButton,
- ToolbarGroup,
-} from './toolbar';
+import { blockSelectionVariants } from "./block-selection";
+import { ResizeHandle } from "./resize-handle";
+import { Toolbar, ToolbarButton, ToolbarGroup } from "./toolbar";
export const TableElement = withHOC(
- TableProvider,
- function TableElement({
- children,
- ...props
- }: PlateElementProps) {
- const readOnly = useReadOnly();
- const isSelectionAreaVisible = usePluginOption(
- BlockSelectionPlugin,
- 'isSelectionAreaVisible'
- );
- const hasControls = !readOnly && !isSelectionAreaVisible;
- const {
- isSelectingCell,
- marginLeft,
- props: tableProps,
- } = useTableElement();
+ TableProvider,
+ function TableElement({ children, ...props }: PlateElementProps) {
+ const readOnly = useReadOnly();
+ const isSelectionAreaVisible = usePluginOption(BlockSelectionPlugin, "isSelectionAreaVisible");
+ const hasControls = !readOnly && !isSelectionAreaVisible;
+ const { isSelectingCell, marginLeft, props: tableProps } = useTableElement();
- const isSelectingTable = useBlockSelected(props.element.id as string);
+ const isSelectingTable = useBlockSelected(props.element.id as string);
- const content = (
-
-
-
+ const content = (
+
+
+
- {isSelectingTable && (
-
- )}
-
-
- );
+ {isSelectingTable &&
}
+
+
+ );
- if (readOnly) {
- return content;
- }
+ if (readOnly) {
+ return content;
+ }
- return {content};
- }
+ return {content};
+ }
);
-function TableFloatingToolbar({
- children,
- ...props
-}: React.ComponentProps) {
- const { tf } = useEditorPlugin(TablePlugin);
- const selected = useSelected();
- const element = useElement();
- const { props: buttonProps } = useRemoveNodeButton({ element });
- const collapsedInside = useEditorSelector(
- (editor) => selected && editor.api.isCollapsed(),
- [selected]
- );
- const isFocusedLast = useFocusedLast();
+function TableFloatingToolbar({ children, ...props }: React.ComponentProps) {
+ const { tf } = useEditorPlugin(TablePlugin);
+ const selected = useSelected();
+ const element = useElement();
+ const { props: buttonProps } = useRemoveNodeButton({ element });
+ const collapsedInside = useEditorSelector(
+ (editor) => selected && editor.api.isCollapsed(),
+ [selected]
+ );
+ const isFocusedLast = useFocusedLast();
- const { canMerge, canSplit } = useTableMergeState();
+ const { canMerge, canSplit } = useTableMergeState();
- return (
-
- {children}
- e.preventDefault()}
- contentEditable={false}
- {...props}
- >
-
-
- {canMerge && (
- tf.table.merge()}
- onMouseDown={(e) => e.preventDefault()}
- tooltip="Merge cells"
- >
-
-
- )}
- {canSplit && (
- tf.table.split()}
- onMouseDown={(e) => e.preventDefault()}
- tooltip="Split cell"
- >
-
-
- )}
+ return (
+
+ {children}
+ e.preventDefault()}
+ contentEditable={false}
+ {...props}
+ >
+
+
+ {canMerge && (
+ tf.table.merge()}
+ onMouseDown={(e) => e.preventDefault()}
+ tooltip="Merge cells"
+ >
+
+
+ )}
+ {canSplit && (
+ tf.table.split()}
+ onMouseDown={(e) => e.preventDefault()}
+ tooltip="Split cell"
+ >
+
+
+ )}
- {collapsedInside && (
-
-
-
-
-
- )}
-
+ {collapsedInside && (
+
+
+
+
+
+ )}
+
- {collapsedInside && (
-
- {
- tf.insert.tableRow({ before: true });
- }}
- onMouseDown={(e) => e.preventDefault()}
- tooltip="Insert row before"
- >
-
-
- {
- tf.insert.tableRow();
- }}
- onMouseDown={(e) => e.preventDefault()}
- tooltip="Insert row after"
- >
-
-
- {
- tf.remove.tableRow();
- }}
- onMouseDown={(e) => e.preventDefault()}
- tooltip="Delete row"
- >
-
-
-
- )}
+ {collapsedInside && (
+
+ {
+ tf.insert.tableRow({ before: true });
+ }}
+ onMouseDown={(e) => e.preventDefault()}
+ tooltip="Insert row before"
+ >
+
+
+ {
+ tf.insert.tableRow();
+ }}
+ onMouseDown={(e) => e.preventDefault()}
+ tooltip="Insert row after"
+ >
+
+
+ {
+ tf.remove.tableRow();
+ }}
+ onMouseDown={(e) => e.preventDefault()}
+ tooltip="Delete row"
+ >
+
+
+
+ )}
- {collapsedInside && (
-
- {
- tf.insert.tableColumn({ before: true });
- }}
- onMouseDown={(e) => e.preventDefault()}
- tooltip="Insert column before"
- >
-
-
- {
- tf.insert.tableColumn();
- }}
- onMouseDown={(e) => e.preventDefault()}
- tooltip="Insert column after"
- >
-
-
- {
- tf.remove.tableColumn();
- }}
- onMouseDown={(e) => e.preventDefault()}
- tooltip="Delete column"
- >
-
-
-
- )}
-
-
-
- );
+ {collapsedInside && (
+
+ {
+ tf.insert.tableColumn({ before: true });
+ }}
+ onMouseDown={(e) => e.preventDefault()}
+ tooltip="Insert column before"
+ >
+
+
+ {
+ tf.insert.tableColumn();
+ }}
+ onMouseDown={(e) => e.preventDefault()}
+ tooltip="Insert column after"
+ >
+
+
+ {
+ tf.remove.tableColumn();
+ }}
+ onMouseDown={(e) => e.preventDefault()}
+ tooltip="Delete column"
+ >
+
+
+
+ )}
+
+
+
+ );
}
-export function TableRowElement({
- children,
- ...props
-}: PlateElementProps) {
- const { element } = props;
- const readOnly = useReadOnly();
- const selected = useSelected();
- const editor = useEditorRef();
- const isSelectionAreaVisible = usePluginOption(
- BlockSelectionPlugin,
- 'isSelectionAreaVisible'
- );
- const hasControls = !readOnly && !isSelectionAreaVisible;
+export function TableRowElement({ children, ...props }: PlateElementProps) {
+ const { element } = props;
+ const readOnly = useReadOnly();
+ const selected = useSelected();
+ const editor = useEditorRef();
+ const isSelectionAreaVisible = usePluginOption(BlockSelectionPlugin, "isSelectionAreaVisible");
+ const hasControls = !readOnly && !isSelectionAreaVisible;
- const { isDragging, nodeRef, previewRef, handleRef } = useDraggable({
- element,
- type: element.type,
- canDropNode: ({ dragEntry, dropEntry }) =>
- PathApi.equals(
- PathApi.parent(dragEntry[1]),
- PathApi.parent(dropEntry[1])
- ),
- onDropHandler: (_, { dragItem }) => {
- const dragElement = (dragItem as { element: TElement }).element;
+ const { isDragging, nodeRef, previewRef, handleRef } = useDraggable({
+ element,
+ type: element.type,
+ canDropNode: ({ dragEntry, dropEntry }) =>
+ PathApi.equals(PathApi.parent(dragEntry[1]), PathApi.parent(dropEntry[1])),
+ onDropHandler: (_, { dragItem }) => {
+ const dragElement = (dragItem as { element: TElement }).element;
- if (dragElement) {
- editor.tf.select(dragElement);
- }
- },
- });
+ if (dragElement) {
+ editor.tf.select(dragElement);
+ }
+ },
+ });
- return (
-
- {hasControls && (
-
-
-
- |
- )}
+ return (
+
+ {hasControls && (
+
+
+
+ |
+ )}
- {children}
-
- );
+ {children}
+
+ );
}
function RowDragHandle({ dragRef }: { dragRef: React.Ref }) {
- const editor = useEditorRef();
- const element = useElement();
+ const editor = useEditorRef();
+ const element = useElement();
- return (
-
- );
+ return (
+
+ );
}
function RowDropLine() {
- const { dropLine } = useDropLine();
+ const { dropLine } = useDropLine();
- if (!dropLine) return null;
+ if (!dropLine) return null;
- return (
-
- );
+ return (
+
+ );
}
export function TableCellElement({
- isHeader,
- ...props
+ isHeader,
+ ...props
}: PlateElementProps & {
- isHeader?: boolean;
+ isHeader?: boolean;
}) {
- const { api } = useEditorPlugin(TablePlugin);
- const readOnly = useReadOnly();
- const element = props.element;
+ const { api } = useEditorPlugin(TablePlugin);
+ const readOnly = useReadOnly();
+ const element = props.element;
- const tableId = useElementSelector(([node]) => node.id as string, [], {
- key: KEYS.table,
- });
- const rowId = useElementSelector(([node]) => node.id as string, [], {
- key: KEYS.tr,
- });
- const isSelectingTable = useBlockSelected(tableId);
- const isSelectingRow = useBlockSelected(rowId) || isSelectingTable;
- const isSelectionAreaVisible = usePluginOption(
- BlockSelectionPlugin,
- 'isSelectionAreaVisible'
- );
+ const tableId = useElementSelector(([node]) => node.id as string, [], {
+ key: KEYS.table,
+ });
+ const rowId = useElementSelector(([node]) => node.id as string, [], {
+ key: KEYS.tr,
+ });
+ const isSelectingTable = useBlockSelected(tableId);
+ const isSelectingRow = useBlockSelected(rowId) || isSelectingTable;
+ const isSelectionAreaVisible = usePluginOption(BlockSelectionPlugin, "isSelectionAreaVisible");
- const { borders, colIndex, colSpan, minHeight, rowIndex, selected, width } =
- useTableCellElement();
+ const { borders, colIndex, colSpan, minHeight, rowIndex, selected, width } =
+ useTableCellElement();
- const { bottomProps, hiddenLeft, leftProps, rightProps } =
- useTableCellElementResizable({
- colIndex,
- colSpan,
- rowIndex,
- });
+ const { bottomProps, hiddenLeft, leftProps, rightProps } = useTableCellElementResizable({
+ colIndex,
+ colSpan,
+ rowIndex,
+ });
- return (
-
-
- {props.children}
-
+ return (
+
+
+ {props.children}
+
- {!isSelectionAreaVisible && (
-
- {!readOnly && (
- <>
-
-
- {!hiddenLeft && (
-
- )}
+ {!isSelectionAreaVisible && (
+
+ {!readOnly && (
+ <>
+
+
+ {!hiddenLeft && (
+
+ )}
-
- {colIndex === 0 && (
-
- )}
- >
- )}
-
- )}
+
+ {colIndex === 0 && (
+
+ )}
+ >
+ )}
+
+ )}
- {isSelectingRow && (
-
- )}
-
- );
+ {isSelectingRow && }
+
+ );
}
-export function TableCellHeaderElement(
- props: React.ComponentProps
-) {
- return ;
+export function TableCellHeaderElement(props: React.ComponentProps) {
+ return ;
}
-const columnResizeVariants = cva('fade-in hidden animate-in', {
- variants: {
- colIndex: {
- 0: 'group-has-[[data-col="0"]:hover]/table:block group-has-[[data-col="0"][data-resizing="true"]]/table:block',
- 1: 'group-has-[[data-col="1"]:hover]/table:block group-has-[[data-col="1"][data-resizing="true"]]/table:block',
- 2: 'group-has-[[data-col="2"]:hover]/table:block group-has-[[data-col="2"][data-resizing="true"]]/table:block',
- 3: 'group-has-[[data-col="3"]:hover]/table:block group-has-[[data-col="3"][data-resizing="true"]]/table:block',
- 4: 'group-has-[[data-col="4"]:hover]/table:block group-has-[[data-col="4"][data-resizing="true"]]/table:block',
- 5: 'group-has-[[data-col="5"]:hover]/table:block group-has-[[data-col="5"][data-resizing="true"]]/table:block',
- 6: 'group-has-[[data-col="6"]:hover]/table:block group-has-[[data-col="6"][data-resizing="true"]]/table:block',
- 7: 'group-has-[[data-col="7"]:hover]/table:block group-has-[[data-col="7"][data-resizing="true"]]/table:block',
- 8: 'group-has-[[data-col="8"]:hover]/table:block group-has-[[data-col="8"][data-resizing="true"]]/table:block',
- 9: 'group-has-[[data-col="9"]:hover]/table:block group-has-[[data-col="9"][data-resizing="true"]]/table:block',
- 10: 'group-has-[[data-col="10"]:hover]/table:block group-has-[[data-col="10"][data-resizing="true"]]/table:block',
- },
- },
+const columnResizeVariants = cva("fade-in hidden animate-in", {
+ variants: {
+ colIndex: {
+ 0: 'group-has-[[data-col="0"]:hover]/table:block group-has-[[data-col="0"][data-resizing="true"]]/table:block',
+ 1: 'group-has-[[data-col="1"]:hover]/table:block group-has-[[data-col="1"][data-resizing="true"]]/table:block',
+ 2: 'group-has-[[data-col="2"]:hover]/table:block group-has-[[data-col="2"][data-resizing="true"]]/table:block',
+ 3: 'group-has-[[data-col="3"]:hover]/table:block group-has-[[data-col="3"][data-resizing="true"]]/table:block',
+ 4: 'group-has-[[data-col="4"]:hover]/table:block group-has-[[data-col="4"][data-resizing="true"]]/table:block',
+ 5: 'group-has-[[data-col="5"]:hover]/table:block group-has-[[data-col="5"][data-resizing="true"]]/table:block',
+ 6: 'group-has-[[data-col="6"]:hover]/table:block group-has-[[data-col="6"][data-resizing="true"]]/table:block',
+ 7: 'group-has-[[data-col="7"]:hover]/table:block group-has-[[data-col="7"][data-resizing="true"]]/table:block',
+ 8: 'group-has-[[data-col="8"]:hover]/table:block group-has-[[data-col="8"][data-resizing="true"]]/table:block',
+ 9: 'group-has-[[data-col="9"]:hover]/table:block group-has-[[data-col="9"][data-resizing="true"]]/table:block',
+ 10: 'group-has-[[data-col="10"]:hover]/table:block group-has-[[data-col="10"][data-resizing="true"]]/table:block',
+ },
+ },
});
diff --git a/surfsense_web/components/ui/toggle-node.tsx b/surfsense_web/components/ui/toggle-node.tsx
index 0c23563aa..167640af2 100644
--- a/surfsense_web/components/ui/toggle-node.tsx
+++ b/surfsense_web/components/ui/toggle-node.tsx
@@ -1,39 +1,33 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import { useToggleButton, useToggleButtonState } from '@platejs/toggle/react';
-import { ChevronRightIcon } from 'lucide-react';
-import { type PlateElementProps, PlateElement } from 'platejs/react';
+import { useToggleButton, useToggleButtonState } from "@platejs/toggle/react";
+import { ChevronRightIcon } from "lucide-react";
+import { type PlateElementProps, PlateElement } from "platejs/react";
-import { cn } from '@/lib/utils';
+import { cn } from "@/lib/utils";
-export function ToggleElement({
- children,
- ...props
-}: PlateElementProps) {
- const element = props.element;
- const state = useToggleButtonState(element.id as string);
- const { buttonProps, open } = useToggleButton(state);
+export function ToggleElement({ children, ...props }: PlateElementProps) {
+ const element = props.element;
+ const state = useToggleButtonState(element.id as string);
+ const { buttonProps, open } = useToggleButton(state);
- return (
-
-
- {children}
-
- );
+ return (
+
+
+ {children}
+
+ );
}
diff --git a/surfsense_web/components/ui/toolbar.tsx b/surfsense_web/components/ui/toolbar.tsx
index 100baa552..67e75a892 100644
--- a/surfsense_web/components/ui/toolbar.tsx
+++ b/surfsense_web/components/ui/toolbar.tsx
@@ -1,387 +1,363 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import * as ToolbarPrimitive from '@radix-ui/react-toolbar';
-import * as TooltipPrimitive from '@radix-ui/react-tooltip';
-import { type VariantProps, cva } from 'class-variance-authority';
-import { ChevronDown } from 'lucide-react';
+import * as ToolbarPrimitive from "@radix-ui/react-toolbar";
+import * as TooltipPrimitive from "@radix-ui/react-tooltip";
+import { type VariantProps, cva } from "class-variance-authority";
+import { ChevronDown } from "lucide-react";
import {
- DropdownMenuLabel,
- DropdownMenuRadioGroup,
- DropdownMenuSeparator,
-} from '@/components/ui/dropdown-menu';
-import { Separator } from '@/components/ui/separator';
-import { Tooltip, TooltipTrigger } from '@/components/ui/tooltip';
-import { cn } from '@/lib/utils';
+ DropdownMenuLabel,
+ DropdownMenuRadioGroup,
+ DropdownMenuSeparator,
+} from "@/components/ui/dropdown-menu";
+import { Separator } from "@/components/ui/separator";
+import { Tooltip, TooltipTrigger } from "@/components/ui/tooltip";
+import { cn } from "@/lib/utils";
export function Toolbar({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
- );
+ return (
+
+ );
}
export function ToolbarToggleGroup({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
- );
+ return (
+
+ );
}
export function ToolbarLink({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
- );
+ return (
+
+ );
}
export function ToolbarSeparator({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
- );
+ return (
+
+ );
}
// From toggleVariants
const toolbarButtonVariants = cva(
- "inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm outline-none transition-[color,box-shadow] hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-checked:bg-accent aria-checked:text-accent-foreground aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
- {
- defaultVariants: {
- size: 'default',
- variant: 'default',
- },
- variants: {
- size: {
- default: 'h-9 min-w-9 px-2',
- lg: 'h-10 min-w-10 px-2.5',
- sm: 'h-8 min-w-8 px-1.5',
- },
- variant: {
- default: 'bg-transparent',
- outline:
- 'border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground',
- },
- },
- }
+ "inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm outline-none transition-[color,box-shadow] hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-checked:bg-accent aria-checked:text-accent-foreground aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
+ {
+ defaultVariants: {
+ size: "default",
+ variant: "default",
+ },
+ variants: {
+ size: {
+ default: "h-9 min-w-9 px-2",
+ lg: "h-10 min-w-10 px-2.5",
+ sm: "h-8 min-w-8 px-1.5",
+ },
+ variant: {
+ default: "bg-transparent",
+ outline:
+ "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
+ },
+ },
+ }
);
const dropdownArrowVariants = cva(
- cn(
- 'inline-flex items-center justify-center rounded-r-md font-medium text-foreground text-sm transition-colors disabled:pointer-events-none disabled:opacity-50'
- ),
- {
- defaultVariants: {
- size: 'sm',
- variant: 'default',
- },
- variants: {
- size: {
- default: 'h-9 w-6',
- lg: 'h-10 w-8',
- sm: 'h-8 w-4',
- },
- variant: {
- default:
- 'bg-transparent hover:bg-muted hover:text-muted-foreground aria-checked:bg-accent aria-checked:text-accent-foreground',
- outline:
- 'border border-input border-l-0 bg-transparent hover:bg-accent hover:text-accent-foreground',
- },
- },
- }
+ cn(
+ "inline-flex items-center justify-center rounded-r-md font-medium text-foreground text-sm transition-colors disabled:pointer-events-none disabled:opacity-50"
+ ),
+ {
+ defaultVariants: {
+ size: "sm",
+ variant: "default",
+ },
+ variants: {
+ size: {
+ default: "h-9 w-6",
+ lg: "h-10 w-8",
+ sm: "h-8 w-4",
+ },
+ variant: {
+ default:
+ "bg-transparent hover:bg-muted hover:text-muted-foreground aria-checked:bg-accent aria-checked:text-accent-foreground",
+ outline:
+ "border border-input border-l-0 bg-transparent hover:bg-accent hover:text-accent-foreground",
+ },
+ },
+ }
);
type ToolbarButtonProps = {
- isDropdown?: boolean;
- pressed?: boolean;
-} & Omit<
- React.ComponentPropsWithoutRef,
- 'asChild' | 'value'
-> &
- VariantProps;
+ isDropdown?: boolean;
+ pressed?: boolean;
+} & Omit, "asChild" | "value"> &
+ VariantProps;
export const ToolbarButton = withTooltip(function ToolbarButton({
- children,
- className,
- isDropdown,
- pressed,
- size = 'sm',
- variant,
- ...props
+ children,
+ className,
+ isDropdown,
+ pressed,
+ size = "sm",
+ variant,
+ ...props
}: ToolbarButtonProps) {
- return typeof pressed === 'boolean' ? (
-
-
- {isDropdown ? (
- <>
-
- {children}
-
-
-
-
- >
- ) : (
- children
- )}
-
-
- ) : (
-
- {children}
-
- );
+ return typeof pressed === "boolean" ? (
+
+
+ {isDropdown ? (
+ <>
+ {children}
+
+
+
+ >
+ ) : (
+ children
+ )}
+
+
+ ) : (
+
+ {children}
+
+ );
});
export function ToolbarSplitButton({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentPropsWithoutRef) {
- return (
-
- );
+ return (
+
+ );
}
type ToolbarSplitButtonPrimaryProps = Omit<
- React.ComponentPropsWithoutRef,
- 'value'
+ React.ComponentPropsWithoutRef,
+ "value"
> &
- VariantProps;
+ VariantProps;
export function ToolbarSplitButtonPrimary({
- children,
- className,
- size = 'sm',
- variant,
- ...props
+ children,
+ className,
+ size = "sm",
+ variant,
+ ...props
}: ToolbarSplitButtonPrimaryProps) {
- return (
-
- {children}
-
- );
+ return (
+
+ {children}
+
+ );
}
export function ToolbarSplitButtonSecondary({
- className,
- size,
- variant,
- ...props
-}: React.ComponentPropsWithoutRef<'span'> &
- VariantProps) {
- return (
- e.stopPropagation()}
- role="button"
- {...props}
- >
-
-
- );
+ className,
+ size,
+ variant,
+ ...props
+}: React.ComponentPropsWithoutRef<"span"> & VariantProps) {
+ return (
+ e.stopPropagation()}
+ role="button"
+ {...props}
+ >
+
+
+ );
}
export function ToolbarToggleItem({
- className,
- size = 'sm',
- variant,
- ...props
+ className,
+ size = "sm",
+ variant,
+ ...props
}: React.ComponentProps &
- VariantProps) {
- return (
-
- );
+ VariantProps) {
+ return (
+
+ );
}
-export function ToolbarGroup({
- children,
- className,
-}: React.ComponentProps<'div'>) {
- return (
-
-
{children}
+export function ToolbarGroup({ children, className }: React.ComponentProps<"div">) {
+ return (
+
- );
+
+
+
+
+ );
}
type TooltipProps = {
- tooltip?: React.ReactNode;
- tooltipContentProps?: Omit<
- React.ComponentPropsWithoutRef,
- 'children'
- >;
- tooltipProps?: Omit<
- React.ComponentPropsWithoutRef,
- 'children'
- >;
- tooltipTriggerProps?: React.ComponentPropsWithoutRef;
+ tooltip?: React.ReactNode;
+ tooltipContentProps?: Omit, "children">;
+ tooltipProps?: Omit, "children">;
+ tooltipTriggerProps?: React.ComponentPropsWithoutRef;
} & React.ComponentProps;
function withTooltip(Component: T) {
- return function ExtendComponent({
- tooltip,
- tooltipContentProps,
- tooltipProps,
- tooltipTriggerProps,
- ...props
- }: TooltipProps) {
- const [mounted, setMounted] = React.useState(false);
+ return function ExtendComponent({
+ tooltip,
+ tooltipContentProps,
+ tooltipProps,
+ tooltipTriggerProps,
+ ...props
+ }: TooltipProps) {
+ const [mounted, setMounted] = React.useState(false);
- React.useEffect(() => {
- setMounted(true);
- }, []);
+ React.useEffect(() => {
+ setMounted(true);
+ }, []);
- const component = )} />;
+ const component = )} />;
- if (tooltip && mounted) {
- return (
-
-
- {component}
-
+ if (tooltip && mounted) {
+ return (
+
+
+ {component}
+
- {tooltip}
-
- );
- }
+ {tooltip}
+
+ );
+ }
- return component;
- };
+ return component;
+ };
}
function TooltipContent({
- children,
- className,
- sideOffset = 4,
- ...props
+ children,
+ className,
+ sideOffset = 4,
+ ...props
}: React.ComponentProps) {
- return (
-
-
- {children}
-
-
- );
+ return (
+
+
+ {children}
+
+
+ );
}
export function ToolbarMenuGroup({
- children,
- className,
- label,
- ...props
+ children,
+ className,
+ label,
+ ...props
}: React.ComponentProps & { label?: string }) {
- return (
- <>
-
+ return (
+ <>
+
-
- {label && (
-
- {label}
-
- )}
- {children}
-
- >
- );
+
+ {label && (
+
+ {label}
+
+ )}
+ {children}
+
+ >
+ );
}
diff --git a/surfsense_web/components/ui/tooltip.tsx b/surfsense_web/components/ui/tooltip.tsx
index 63b5a4eed..09fc23448 100644
--- a/surfsense_web/components/ui/tooltip.tsx
+++ b/surfsense_web/components/ui/tooltip.tsx
@@ -1,62 +1,58 @@
-"use client"
+"use client";
-import * as React from "react"
-import { Tooltip as TooltipPrimitive } from "radix-ui"
+import * as React from "react";
+import { Tooltip as TooltipPrimitive } from "radix-ui";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
function TooltipProvider({
- delayDuration = 0,
- disableHoverableContent = true,
- ...props
+ delayDuration = 0,
+ disableHoverableContent = true,
+ ...props
}: React.ComponentProps) {
- return (
-
- )
+ return (
+
+ );
}
-function Tooltip({
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
- )
+function Tooltip({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+ );
}
-function TooltipTrigger({
- ...props
-}: React.ComponentProps) {
- return
+function TooltipTrigger({ ...props }: React.ComponentProps) {
+ return ;
}
function TooltipContent({
- className,
- sideOffset = 4,
- children,
- ...props
+ className,
+ sideOffset = 4,
+ children,
+ ...props
}: React.ComponentProps) {
- return (
-
-
- {children}
-
-
- )
+ return (
+
+
+ {children}
+
+
+ );
}
-export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
+export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
diff --git a/surfsense_web/components/ui/turn-into-toolbar-button.tsx b/surfsense_web/components/ui/turn-into-toolbar-button.tsx
index 94b77529d..3e1b534f9 100644
--- a/surfsense_web/components/ui/turn-into-toolbar-button.tsx
+++ b/surfsense_web/components/ui/turn-into-toolbar-button.tsx
@@ -1,191 +1,189 @@
-'use client';
+"use client";
-import * as React from 'react';
+import * as React from "react";
-import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
-import type { TElement } from 'platejs';
+import type { DropdownMenuProps } from "@radix-ui/react-dropdown-menu";
+import type { TElement } from "platejs";
-import { DropdownMenuItemIndicator } from '@radix-ui/react-dropdown-menu';
+import { DropdownMenuItemIndicator } from "@radix-ui/react-dropdown-menu";
import {
- CheckIcon,
- ChevronRightIcon,
- FileCodeIcon,
- Heading1Icon,
- Heading2Icon,
- Heading3Icon,
- Heading4Icon,
- Heading5Icon,
- Heading6Icon,
- InfoIcon,
- ListIcon,
- ListOrderedIcon,
- PilcrowIcon,
- QuoteIcon,
- SquareIcon,
-} from 'lucide-react';
-import { KEYS } from 'platejs';
-import { useEditorRef, useSelectionFragmentProp } from 'platejs/react';
+ CheckIcon,
+ ChevronRightIcon,
+ FileCodeIcon,
+ Heading1Icon,
+ Heading2Icon,
+ Heading3Icon,
+ Heading4Icon,
+ Heading5Icon,
+ Heading6Icon,
+ InfoIcon,
+ ListIcon,
+ ListOrderedIcon,
+ PilcrowIcon,
+ QuoteIcon,
+ SquareIcon,
+} from "lucide-react";
+import { KEYS } from "platejs";
+import { useEditorRef, useSelectionFragmentProp } from "platejs/react";
import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuRadioItem,
- DropdownMenuTrigger,
-} from '@/components/ui/dropdown-menu';
-import {
- getBlockType,
- setBlockType,
-} from '@/components/editor/transforms';
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuRadioItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { getBlockType, setBlockType } from "@/components/editor/transforms";
-import { ToolbarButton, ToolbarMenuGroup } from './toolbar';
+import { ToolbarButton, ToolbarMenuGroup } from "./toolbar";
export const turnIntoItems = [
- {
- icon: ,
- keywords: ['paragraph'],
- label: 'Text',
- value: KEYS.p,
- },
- {
- icon: ,
- keywords: ['title', 'h1'],
- label: 'Heading 1',
- value: 'h1',
- },
- {
- icon: ,
- keywords: ['subtitle', 'h2'],
- label: 'Heading 2',
- value: 'h2',
- },
- {
- icon: ,
- keywords: ['subtitle', 'h3'],
- label: 'Heading 3',
- value: 'h3',
- },
- {
- icon: ,
- keywords: ['subtitle', 'h4'],
- label: 'Heading 4',
- value: 'h4',
- },
- {
- icon: ,
- keywords: ['subtitle', 'h5'],
- label: 'Heading 5',
- value: 'h5',
- },
- {
- icon: ,
- keywords: ['subtitle', 'h6'],
- label: 'Heading 6',
- value: 'h6',
- },
- {
- icon: ,
- keywords: ['unordered', 'ul', '-'],
- label: 'Bulleted list',
- value: KEYS.ul,
- },
- {
- icon: ,
- keywords: ['ordered', 'ol', '1'],
- label: 'Numbered list',
- value: KEYS.ol,
- },
- {
- icon: ,
- keywords: ['checklist', 'task', 'checkbox', '[]'],
- label: 'To-do list',
- value: KEYS.listTodo,
- },
- {
- icon: ,
- keywords: ['```'],
- label: 'Code',
- value: KEYS.codeBlock,
- },
- {
- icon: ,
- keywords: ['citation', 'blockquote', '>'],
- label: 'Quote',
- value: KEYS.blockquote,
- },
- {
- icon: ,
- keywords: ['callout', 'note', 'info', 'warning', 'tip'],
- label: 'Callout',
- value: KEYS.callout,
- },
- {
- icon: ,
- keywords: ['toggle', 'collapsible', 'expand'],
- label: 'Toggle',
- value: KEYS.toggle,
- },
+ {
+ icon: ,
+ keywords: ["paragraph"],
+ label: "Text",
+ value: KEYS.p,
+ },
+ {
+ icon: ,
+ keywords: ["title", "h1"],
+ label: "Heading 1",
+ value: "h1",
+ },
+ {
+ icon: ,
+ keywords: ["subtitle", "h2"],
+ label: "Heading 2",
+ value: "h2",
+ },
+ {
+ icon: ,
+ keywords: ["subtitle", "h3"],
+ label: "Heading 3",
+ value: "h3",
+ },
+ {
+ icon: ,
+ keywords: ["subtitle", "h4"],
+ label: "Heading 4",
+ value: "h4",
+ },
+ {
+ icon: ,
+ keywords: ["subtitle", "h5"],
+ label: "Heading 5",
+ value: "h5",
+ },
+ {
+ icon: ,
+ keywords: ["subtitle", "h6"],
+ label: "Heading 6",
+ value: "h6",
+ },
+ {
+ icon: ,
+ keywords: ["unordered", "ul", "-"],
+ label: "Bulleted list",
+ value: KEYS.ul,
+ },
+ {
+ icon: ,
+ keywords: ["ordered", "ol", "1"],
+ label: "Numbered list",
+ value: KEYS.ol,
+ },
+ {
+ icon: ,
+ keywords: ["checklist", "task", "checkbox", "[]"],
+ label: "To-do list",
+ value: KEYS.listTodo,
+ },
+ {
+ icon: ,
+ keywords: ["```"],
+ label: "Code",
+ value: KEYS.codeBlock,
+ },
+ {
+ icon: ,
+ keywords: ["citation", "blockquote", ">"],
+ label: "Quote",
+ value: KEYS.blockquote,
+ },
+ {
+ icon: ,
+ keywords: ["callout", "note", "info", "warning", "tip"],
+ label: "Callout",
+ value: KEYS.callout,
+ },
+ {
+ icon: ,
+ keywords: ["toggle", "collapsible", "expand"],
+ label: "Toggle",
+ value: KEYS.toggle,
+ },
];
-export function TurnIntoToolbarButton({ tooltip = 'Turn into', ...props }: DropdownMenuProps & { tooltip?: React.ReactNode }) {
- const editor = useEditorRef();
- const [open, setOpen] = React.useState(false);
+export function TurnIntoToolbarButton({
+ tooltip = "Turn into",
+ ...props
+}: DropdownMenuProps & { tooltip?: React.ReactNode }) {
+ const editor = useEditorRef();
+ const [open, setOpen] = React.useState(false);
- const value = useSelectionFragmentProp({
- defaultValue: KEYS.p,
- getProp: (node) => getBlockType(node as TElement),
- });
- const selectedItem = React.useMemo(
- () =>
- turnIntoItems.find((item) => item.value === (value ?? KEYS.p)) ??
- turnIntoItems[0],
- [value]
- );
+ const value = useSelectionFragmentProp({
+ defaultValue: KEYS.p,
+ getProp: (node) => getBlockType(node as TElement),
+ });
+ const selectedItem = React.useMemo(
+ () => turnIntoItems.find((item) => item.value === (value ?? KEYS.p)) ?? turnIntoItems[0],
+ [value]
+ );
- return (
-
-
-
- {selectedItem.label}
-
-
+ return (
+
+
+
+ {selectedItem.label}
+
+
- {
- e.preventDefault();
- editor.tf.focus();
- }}
- align="start"
- >
- {
- setBlockType(editor, type);
- }}
- label="Turn into"
- >
- {turnIntoItems.map(({ icon, label, value: itemValue }) => (
-
-
-
-
-
-
- {icon}
- {label}
-
- ))}
-
-
-
- );
+ {
+ e.preventDefault();
+ editor.tf.focus();
+ }}
+ align="start"
+ >
+ {
+ setBlockType(editor, type);
+ }}
+ label="Turn into"
+ >
+ {turnIntoItems.map(({ icon, label, value: itemValue }) => (
+
+
+
+
+
+
+ {icon}
+ {label}
+
+ ))}
+
+
+
+ );
}
diff --git a/surfsense_web/hooks/use-debounce.ts b/surfsense_web/hooks/use-debounce.ts
index fd60d32a7..c74052e38 100644
--- a/surfsense_web/hooks/use-debounce.ts
+++ b/surfsense_web/hooks/use-debounce.ts
@@ -1,18 +1,18 @@
-import * as React from 'react';
+import * as React from "react";
export const useDebounce = (value: T, delay = 500) => {
- const [debouncedValue, setDebouncedValue] = React.useState(value);
+ const [debouncedValue, setDebouncedValue] = React.useState(value);
- React.useEffect(() => {
- const handler: NodeJS.Timeout = setTimeout(() => {
- setDebouncedValue(value);
- }, delay);
+ React.useEffect(() => {
+ const handler: NodeJS.Timeout = setTimeout(() => {
+ setDebouncedValue(value);
+ }, delay);
- // Cancel the timeout if value changes (also on delay change or unmount)
- return () => {
- clearTimeout(handler);
- };
- }, [value, delay]);
+ // Cancel the timeout if value changes (also on delay change or unmount)
+ return () => {
+ clearTimeout(handler);
+ };
+ }, [value, delay]);
- return debouncedValue;
+ return debouncedValue;
};
diff --git a/surfsense_web/hooks/use-mounted.ts b/surfsense_web/hooks/use-mounted.ts
index 1bc7bde21..0da879d5b 100644
--- a/surfsense_web/hooks/use-mounted.ts
+++ b/surfsense_web/hooks/use-mounted.ts
@@ -1,11 +1,11 @@
-import * as React from 'react';
+import * as React from "react";
export function useMounted() {
- const [mounted, setMounted] = React.useState(false);
+ const [mounted, setMounted] = React.useState(false);
- React.useEffect(() => {
- setMounted(true);
- }, []);
+ React.useEffect(() => {
+ setMounted(true);
+ }, []);
- return mounted;
+ return mounted;
}