fix: improve notes functionality, update theme colors definition and resolve ui and ux small bugs (#4414)

* Fixed background blur overlay color on dialogs

* Try to fix f5 error

* Fix text color of notes

* Fix bg color of nodenode to first one available if not existent

* Removed unused imports

* Added new node colors

* Updated color of placeholder note node

* Fixed notes to appear as soon as when the add button is clicked

* Added transparent option to notes

* Fixed editor design

* Fix design of notes editor

* Update text color on the note node description

* Added canvas dot variable

* Added size and gap definition to background dots

* Updated emerald colors and badge

* used correct badges on tableAutoCellRender

* Used correct colors on the component global variable badge

* Fix color on light mode

* Fixed emerald color on the status of component

* Applied static pink to active icons on navs

* Make sidebar narrower

* Updated size of /

* Fixed Beta and Legacy badges

* Fixed position of chevron

* Fixed node name input

* Updated clear search button

* Updated color for grid dots

* Updated sidebar filter color

* Added chat input check

* Fixed sorting of flow sidebar component

* Fix canvas color

* Updated sticky notes test

* Fixed tests

* Fixed test
This commit is contained in:
Lucas Oliveira 2024-11-06 11:54:43 -03:00 committed by GitHub
commit de7b23ea93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 518 additions and 406 deletions

View file

@ -1,6 +1,7 @@
<!doctype html>
<html lang="en">
<head>
<base href="/" />
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

View file

@ -11,6 +11,7 @@ export default function NodeDescription({
selected,
nodeId,
emptyPlaceholder = "Double Click to Edit Description",
placeholderClassName,
charLimit,
inputClassName,
mdClassName,
@ -20,6 +21,7 @@ export default function NodeDescription({
selected: boolean;
nodeId: string;
emptyPlaceholder?: string;
placeholderClassName?: string;
charLimit?: number;
inputClassName?: string;
mdClassName?: string;
@ -62,7 +64,8 @@ export default function NodeDescription({
className={cn(
!inputDescription ? "overflow-auto" : "",
hasScroll ? "nowheel" : "",
charLimit ? "px-2" : "",
charLimit ? "px-2 pb-4" : "",
"w-full",
)}
>
{inputDescription ? (
@ -114,10 +117,11 @@ export default function NodeDescription({
{charLimit && (
<div
className={cn(
"text-left text-[13px]",
"pt-1 text-left text-[13px]",
(nodeDescription?.length ?? 0) >= charLimit
? "text-error"
: "text-primary",
placeholderClassName,
)}
data-testid="note_char_limit"
>
@ -130,8 +134,9 @@ export default function NodeDescription({
data-testid="generic-node-desc"
ref={overflowRef}
className={cn(
"nodoubleclick generic-node-desc-text h-full cursor-text text-[13px] word-break-break-word dark:text-note-placeholder",
"nodoubleclick generic-node-desc-text h-full cursor-text text-[13px] word-break-break-word",
description === "" || !description ? "font-light italic" : "",
placeholderClassName,
)}
onDoubleClick={(e) => {
setInputDescription(true);
@ -144,7 +149,7 @@ export default function NodeDescription({
<Markdown
linkTarget="_blank"
className={cn(
"markdown prose flex h-full w-full flex-col text-[13px] leading-5 text-muted-foreground word-break-break-word dark:prose-invert",
"markdown prose flex h-full w-full flex-col text-[13px] leading-5 text-foreground word-break-break-word",
mdClassName,
)}
>

View file

@ -1,5 +1,4 @@
import InputComponent from "@/components/inputComponent";
import { BuildStatus } from "@/constants/enums";
import { Input } from "@/components/ui/input";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import useFlowStore from "@/stores/flowStore";
import { VertexBuildTypeAPI } from "@/types/api";
@ -39,7 +38,7 @@ export default function NodeName({
return inputName ? (
<div className="m-[1px] w-full">
<InputComponent
<Input
onBlur={() => {
setInputName(false);
if (nodeName?.trim() !== "") {
@ -60,10 +59,8 @@ export default function NodeName({
}}
value={nodeName}
autoFocus
onChange={setNodeName}
password={false}
blurOnEnter={true}
id={`input-title-${display_name}`}
onChange={(e) => setNodeName(e.target.value)}
data-testid={`input-title-${display_name}`}
/>
</div>
) : (

View file

@ -1,14 +1,13 @@
import { useDarkStore } from "@/stores/darkStore";
import useFlowStore from "@/stores/flowStore";
import { log } from "console";
import { useEffect, useMemo, useRef, useState } from "react";
import { Handle, Position, useViewport } from "reactflow";
import { Handle, Position } from "reactflow";
import ShadTooltip from "../../../../components/shadTooltipComponent";
import {
isValidConnection,
scapedJSONStringfy,
} from "../../../../utils/reactflowUtils";
import { classNames, cn, groupByFamily } from "../../../../utils/utils";
import { cn, groupByFamily } from "../../../../utils/utils";
import HandleTooltipComponent from "../HandleTooltipComponent";
export default function HandleRenderComponent({
@ -42,7 +41,7 @@ export default function HandleRenderComponent({
nodeId: string;
colorName?: string[];
}) {
const handleColorName = colorName?.[0];
const handleColorName = colorName?.[0] ?? "";
const innerColorName = `inner-${handleColorName}`;
const innerForegroundColorName = `${innerColorName}-foreground`;
@ -156,7 +155,7 @@ export default function HandleRenderComponent({
source: undefined,
sourceHandle: undefined,
type: tooltipTitle,
color: colors[0],
color: handleColorName,
}
: {
sourceHandle: myId,
@ -164,7 +163,7 @@ export default function HandleRenderComponent({
target: undefined,
targetHandle: undefined,
type: tooltipTitle,
color: colors[0],
color: handleColorName,
},
[left, myId, nodeId, tooltipTitle, colors],
);

View file

@ -5,15 +5,12 @@ import { usePostValidateComponentCode } from "@/controllers/API/queries/nodes/us
import { useEffect, useMemo, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
import IconComponent, {
ForwardedIconComponent,
} from "../../components/genericIconComponent";
import { ForwardedIconComponent } from "../../components/genericIconComponent";
import ShadTooltip from "../../components/shadTooltipComponent";
import { Button } from "../../components/ui/button";
import {
TOOLTIP_HIDDEN_OUTPUTS,
TOOLTIP_OPEN_HIDDEN_OUTPUTS,
TOOLTIP_OUTDATED_NODE,
} from "../../constants/constants";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import useAlertStore from "../../stores/alertStore";
@ -379,6 +376,7 @@ export default function GenericNode({
<div>
<NodeDescription
description={data.node?.description}
mdClassName={"dark:prose-invert"}
nodeId={data.id}
selected={selected}
/>

View file

@ -17,10 +17,9 @@ import useAlertStore from "@/stores/alertStore";
import useFlowStore from "@/stores/flowStore";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useShortcutsStore } from "@/stores/shortcuts";
import { NodeDataType, noteDataType } from "@/types/flow";
import { noteDataType } from "@/types/flow";
import { classNames, cn, openInNewTab } from "@/utils/utils";
import { cloneDeep, set, take } from "lodash";
import { useState } from "react";
import { cloneDeep } from "lodash";
import IconComponent from "../../../components/genericIconComponent";
export default function NoteToolbarComponent({
@ -30,7 +29,6 @@ export default function NoteToolbarComponent({
data: noteDataType;
bgColor: string;
}) {
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setNoticeData = useAlertStore((state) => state.setNoticeData);
const nodes = useFlowStore((state) => state.nodes);
const setLastCopiedSelection = useFlowStore(
@ -95,9 +93,12 @@ export default function NoteToolbarComponent({
>
<div
style={{
backgroundColor: COLOR_OPTIONS[bgColor],
backgroundColor: COLOR_OPTIONS[bgColor] ?? "#00000000",
}}
className="h-4 w-4 rounded-full"
className={cn(
"h-4 w-4 rounded-full",
COLOR_OPTIONS[bgColor] === null && "border",
)}
></div>
</div>
</div>
@ -131,9 +132,10 @@ export default function NoteToolbarComponent({
className={cn(
"h-4 w-4 rounded-full hover:border hover:border-ring",
bgColor === color ? "border-2 border-blue-500" : "",
code === null && "border",
)}
style={{
backgroundColor: code,
backgroundColor: code ?? "#00000000",
}}
></div>
</Button>

View file

@ -19,7 +19,9 @@ function NoteNode({
selected: boolean;
}) {
const bgColor =
data.node?.template.backgroundColor ?? Object.keys(COLOR_OPTIONS)[0];
Object.keys(COLOR_OPTIONS).find(
(key) => key === data.node?.template.backgroundColor,
) ?? Object.keys(COLOR_OPTIONS)[0];
const nodeDiv = useRef<HTMLDivElement>(null);
const [size, setSize] = useState({ width: 0, height: 0 });
//tricky to start the description with the right size
@ -61,12 +63,13 @@ function NoteNode({
maxWidth: NOTE_NODE_MAX_WIDTH,
minWidth: NOTE_NODE_MIN_WIDTH,
minHeight: NOTE_NODE_MIN_HEIGHT,
backgroundColor: COLOR_OPTIONS[bgColor],
backgroundColor: COLOR_OPTIONS[bgColor] ?? "#00000000",
}}
ref={nodeDiv}
className={cn(
"flex h-full w-full flex-col gap-3 border border-b p-3 transition-all",
selected ? "" : "-z-50 shadow-sm",
"flex h-full w-full flex-col gap-3 rounded-xl p-3 transition-all",
COLOR_OPTIONS[bgColor] !== null &&
`border ${!selected && "-z-50 shadow-sm"}`,
)}
>
<div
@ -77,13 +80,26 @@ function NoteNode({
}}
>
<NodeDescription
inputClassName="border-0 ring-transparent resize-none rounded-none shadow-none h-full w-full"
style={{ backgroundColor: COLOR_OPTIONS[bgColor] }}
inputClassName={cn(
"border-0 ring-transparent resize-none shadow-none rounded-sm h-full w-full",
COLOR_OPTIONS[bgColor] === null
? ""
: "dark:!ring-background dark:text-background",
)}
mdClassName={
COLOR_OPTIONS[bgColor] === null
? "dark:prose-invert"
: "dark:!text-background"
}
style={{ backgroundColor: COLOR_OPTIONS[bgColor] ?? "#00000000" }}
charLimit={2500}
nodeId={data.id}
selected={selected}
description={data.node?.description}
emptyPlaceholder="Double-click to start typing or enter Markdown..."
placeholderClassName={
COLOR_OPTIONS[bgColor] === null ? "" : "dark:!text-background"
}
/>
</div>
</div>

View file

@ -15,7 +15,7 @@ import {
import { cn } from "@/utils/utils";
import { PopoverAnchor } from "@radix-ui/react-popover";
import { X } from "lucide-react";
import React, { useState } from "react";
import { useState } from "react";
const CustomInputPopover = ({
id,
@ -97,11 +97,10 @@ const CustomInputPopover = ({
))
) : selectedOption?.length > 0 ? (
<Badge
variant="secondary"
variant={nodeStyle ? "emerald" : "secondary"}
className={cn(
"flex items-center gap-1 truncate bg-muted",
nodeStyle &&
"font-jetbrains rounded-[3px] bg-emerald-100 px-1 text-emerald-700 hover:bg-emerald-200",
"flex items-center gap-1 truncate",
nodeStyle ? "font-jetbrains rounded-[3px] px-1" : "bg-muted",
)}
>
<div className="max-w-36 truncate">{selectedOption}</div>

View file

@ -40,11 +40,9 @@ export default function TableAutoCellRender({
else if (value === "success") {
return (
<Badge
variant="outline"
variant="successStatic"
size="sq"
className={cn(
"h-[18px] w-full justify-center bg-success-background text-success-foreground hover:bg-success-background",
)}
className={cn("h-[18px] w-full justify-center")}
>
{value}
</Badge>
@ -52,11 +50,9 @@ export default function TableAutoCellRender({
} else if (value === "failure") {
return (
<Badge
variant="outline"
variant="errorStatic"
size="sq"
className={cn(
"h-[18px] w-full justify-center bg-error-background text-error-foreground hover:bg-error-background",
)}
className={cn("h-[18px] w-full justify-center")}
>
{value}
</Badge>

View file

@ -15,15 +15,20 @@ const badgeVariants = cva(
destructive:
"bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground",
outline: "text-primary/80 border-ring/60",
secondaryStatic: "bg-input text-muted-foreground border-0",
secondaryStatic: "bg-muted text-muted-foreground border-0",
pinkStatic: "bg-accent-pink text-accent-pink-foreground border-0",
emerald:
"bg-accent-emerald text-accent-emerald-foreground hover:bg-accent-emerald-hover",
successStatic:
"bg-accent-emerald text-accent-emerald-foreground border-0",
errorStatic: "bg-error-background text-error-foreground border-0",
},
size: {
sm: "h-4 text-xs",
md: "h-5 text-sm",
lg: "h-6 text-base",
sq: "h-6 px-1.5 text-sm font-medium rounded-md",
xq: "h-5 px-1 text-xs font-medium rounded-md",
xq: "h-6 px-1.5 text-xs font-medium rounded-sm",
},
},
defaultVariants: {

View file

@ -25,7 +25,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"nopan nodelete nodrag noflow fixed inset-0 bottom-0 left-0 right-0 top-0 z-50 overflow-auto bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"nopan nodelete nodrag noflow fixed inset-0 bottom-0 left-0 right-0 top-0 z-50 overflow-auto bg-black/50 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}

View file

@ -27,7 +27,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"nopan nodelete nodrag noflow fixed inset-0 bottom-0 left-0 right-0 top-0 z-40 overflow-auto bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"nopan nodelete nodrag noflow fixed inset-0 bottom-0 left-0 right-0 top-0 z-40 overflow-auto bg-black/50 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}

View file

@ -21,7 +21,7 @@ const SheetOverlay = React.forwardRef<
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}

View file

@ -871,7 +871,7 @@ export const LOCATIONS_TO_RETURN = ["/flow/", "/settings/"];
export const MAX_BATCH_SIZE = 50;
export const MODAL_CLASSES =
"nopan nodelete nodrag noflow fixed inset-0 bottom-0 left-0 right-0 top-0 z-50 overflow-auto bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0";
"nopan nodelete nodrag noflow fixed inset-0 bottom-0 left-0 right-0 top-0 z-50 overflow-auto bg-black/50 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0";
export const ALLOWED_IMAGE_INPUT_EXTENSIONS = ["png", "jpg", "jpeg"];
@ -926,19 +926,12 @@ export const NOTE_NODE_MAX_HEIGHT = 800;
export const NOTE_NODE_MAX_WIDTH = 600;
export const COLOR_OPTIONS = {
default: "var(--note-default)",
indigo: "var(--note-indigo)",
emerald: "var(--note-emerald)",
amber: "var(--note-amber)",
red: "var(--note-red)",
};
export const SHADOW_COLOR_OPTIONS = {
default: "var(--note-default-opacity)",
indigo: "var(--note-indigo-opacity)",
emerald: "var(--note-emerald-opacity)",
amber: "var(--note-amber-opacity)",
red: "var(--note-red-opacity)",
amber: "hsl(var(--note-amber))",
neutral: "hsl(var(--note-neutral))",
rose: "hsl(var(--note-rose))",
blue: "hsl(var(--note-blue))",
lime: "hsl(var(--note-lime))",
transparent: null,
};
export const maxSizeFilesInBytes = 10 * 1024 * 1024; // 10MB in bytes

View file

@ -45,7 +45,7 @@ export function SaveChangesModal({
>
<ConfirmationModal.Content>
{autoSave ? (
<div className="mb-4 flex w-full items-center gap-3 rounded-md bg-gray-100 px-4 py-2 text-gray-800 dark:bg-gray-900/40 dark:text-gray-100">
<div className="mb-4 flex w-full items-center gap-3 rounded-md bg-muted px-4 py-2 text-muted-foreground">
<Loading className="h-5 w-5" />
Saving your changes...
</div>

View file

@ -65,7 +65,7 @@ export function Nav({ categories, currentTab, setCurrentTab }: NavProps) {
name={link.icon}
className={`h-4 w-4 stroke-2 ${
currentTab === link.id
? "x-gradient"
? "text-accent-pink-foreground"
: "text-muted-foreground"
}`}
/>

View file

@ -8,9 +8,9 @@ import ForwardedIconComponent from "@/components/genericIconComponent";
import LoadingComponent from "@/components/loadingComponent";
import { SidebarTrigger, useSidebar } from "@/components/ui/sidebar";
import {
COLOR_OPTIONS,
NOTE_NODE_MIN_HEIGHT,
NOTE_NODE_MIN_WIDTH,
SHADOW_COLOR_OPTIONS,
} from "@/constants/constants";
import { useGetBuildsQuery } from "@/controllers/API/queries/_builds";
import { track } from "@/customization/utils/analytics";
@ -196,8 +196,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
const zoomLevel = reactFlowInstance?.getZoom();
const shadowBoxWidth = NOTE_NODE_MIN_WIDTH * (zoomLevel || 1);
const shadowBoxHeight = NOTE_NODE_MIN_HEIGHT * (zoomLevel || 1);
const shadowBoxBackgroundColor =
SHADOW_COLOR_OPTIONS[Object.keys(SHADOW_COLOR_OPTIONS)[0]];
const shadowBoxBackgroundColor = COLOR_OPTIONS[Object.keys(COLOR_OPTIONS)[0]];
function handleGroupNode() {
takeSnapshot();
@ -432,6 +431,11 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
const onDrop = useCallback(
(event: React.DragEvent) => {
event.preventDefault();
const grabbingElement =
document.getElementsByClassName("cursor-grabbing");
if (grabbingElement.length > 0) {
document.body.removeChild(grabbingElement[0]);
}
if (event.dataTransfer.types.some((type) => isSupportedNodeTypes(type))) {
takeSnapshot();
@ -561,20 +565,6 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
[isAddingNote, setNodes, reactFlowInstance, getNodeId, setFilterEdge],
);
const onPaneMouseMove = useCallback(
(event: React.MouseEvent) => {
if (isAddingNote) {
const shadowBox = document.getElementById("shadow-box");
if (shadowBox) {
shadowBox.style.display = "block";
shadowBox.style.left = `${event.clientX - shadowBoxWidth / 2}px`;
shadowBox.style.top = `${event.clientY - shadowBoxHeight / 2}px`;
}
}
},
[isAddingNote],
);
const handleEdgeClick = (event, edge) => {
const color =
nodeColorsName[edge?.data?.targetHandle?.inputTypes[0]] ||
@ -588,6 +578,25 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
const { open } = useSidebar();
useEffect(() => {
const handleGlobalMouseMove = (event) => {
if (isAddingNote) {
const shadowBox = document.getElementById("shadow-box");
if (shadowBox) {
shadowBox.style.display = "block";
shadowBox.style.left = `${event.clientX - shadowBoxWidth / 2}px`;
shadowBox.style.top = `${event.clientY - shadowBoxHeight / 2}px`;
}
}
};
document.addEventListener("mousemove", handleGlobalMouseMove);
return () => {
document.removeEventListener("mousemove", handleGlobalMouseMove);
};
}, [isAddingNote, shadowBoxWidth, shadowBoxHeight]);
return (
<div className="h-full w-full bg-canvas" ref={reactFlowWrapper}>
{showCanvas ? (
@ -625,10 +634,9 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
panActivationKeyCode={""}
proOptions={{ hideAttribution: true }}
onPaneClick={onPaneClick}
onPaneMouseMove={onPaneMouseMove}
onEdgeClick={handleEdgeClick}
>
<Background className="" />
<Background size={2} gap={20} className="" />
{!view && (
<>
<CanvasControls>
@ -637,6 +645,12 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
tooltipText="Add Note"
onClick={() => {
setIsAddingNote(true);
const shadowBox = document.getElementById("shadow-box");
if (shadowBox) {
shadowBox.style.display = "block";
shadowBox.style.left = `${position.current.x - shadowBoxWidth / 2}px`;
shadowBox.style.top = `${position.current.y - shadowBoxHeight / 2}px`;
}
}}
iconClasses="text-primary"
testId="add_note"
@ -678,6 +692,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
width: `${shadowBoxWidth}px`,
height: `${shadowBoxHeight}px`,
backgroundColor: `${shadowBoxBackgroundColor}`,
opacity: 0.7,
pointerEvents: "none",
}}
></div>

View file

@ -5,18 +5,25 @@ import { Button } from "@/components/ui/button";
export function SidebarFilterComponent({
isInput,
type,
color,
resetFilters,
}: {
isInput: boolean;
type: string;
color: string;
resetFilters: () => void;
}) {
return (
<div className="mb-0.5 flex w-full items-center justify-between rounded border bg-accent-indigo p-2 text-sm text-foreground">
<div
className={`mb-0.5 flex w-full items-center justify-between rounded border p-2 text-sm text-foreground`}
style={{
backgroundColor: `hsl(var(--inner-${color}-foreground))`,
}}
>
<div className="flex flex-1 items-center gap-1.5">
<ForwardedIconComponent
name="ListFilter"
className="h-4 w-4 shrink-0 stroke-2 text-accent-indigo-foreground"
className={`h-4 w-4 shrink-0 stroke-2`}
/>
<div className="flex flex-1">
{isInput ? "Input" : "Output"}:{" "}

View file

@ -37,6 +37,8 @@ export const SidebarDraggableComponent = forwardRef(
official,
beta,
legacy,
disabled,
disabledTooltip,
}: {
sectionName: string;
apiClass: APIClassType;
@ -49,6 +51,8 @@ export const SidebarDraggableComponent = forwardRef(
official: boolean;
beta: boolean;
legacy: boolean;
disabled?: boolean;
disabledTooltip?: string;
},
ref,
) => {
@ -103,116 +107,129 @@ export const SidebarDraggableComponent = forwardRef(
open={open}
key={itemName}
>
<div
onPointerDown={handlePointerDown}
onContextMenuCapture={(e) => {
e.preventDefault();
setOpen(true);
}}
key={itemName}
data-tooltip-id={itemName}
tabIndex={0}
onKeyDown={handleKeyDown}
className="rounded-md outline-none ring-ring focus-visible:ring-2"
<ShadTooltip
content={disabled ? disabledTooltip : null}
styleClasses="z-50"
>
<div
data-testid={sectionName + display_name}
id={sectionName + display_name}
className={cn(
"group/draggable flex cursor-grab items-center gap-2 rounded-md bg-muted p-3 hover:bg-secondary-hover/75",
error ? "cursor-not-allowed select-none" : "",
)}
draggable={!error}
style={{
borderLeftColor: color,
}}
onDragStart={onDragStart}
onDragEnd={() => {
document.body.removeChild(
document.getElementsByClassName("cursor-grabbing")[0],
);
onPointerDown={handlePointerDown}
onContextMenuCapture={(e) => {
e.preventDefault();
setOpen(true);
}}
key={itemName}
data-tooltip-id={itemName}
tabIndex={0}
onKeyDown={handleKeyDown}
className="rounded-md outline-none ring-ring focus-visible:ring-2"
>
<ForwardedIconComponent name={icon} className="h-5 w-5 shrink-0" />
<div className="flex flex-1 items-center overflow-hidden">
<ShadTooltip content={display_name} styleClasses="z-50">
<span className="truncate text-sm font-semibold">
{display_name}
</span>
</ShadTooltip>
{beta && (
<Badge
variant="pinkStatic"
size="sq"
className="ml-1.5 shrink-0"
>
BETA
</Badge>
<div
data-testid={sectionName + display_name}
id={sectionName + display_name}
className={cn(
"group/draggable flex cursor-grab items-center gap-2 rounded-md bg-muted p-3 hover:bg-secondary-hover/75",
error && "cursor-not-allowed select-none",
disabled
? "pointer-events-none bg-accent text-placeholder-foreground"
: "bg-muted text-foreground",
)}
{legacy && (
<Badge
variant="secondaryStatic"
size="sq"
className="ml-1.5 shrink-0"
>
LEGACY
</Badge>
)}
</div>
<div className="flex shrink-0 items-center gap-1">
<Button
variant="ghost"
size="icon"
tabIndex={-1}
className="text-primary"
onClick={() => addComponent(apiClass, itemName)}
>
<ForwardedIconComponent
name="Plus"
className="h-4 w-4 shrink-0 transition-all group-hover/draggable:opacity-100 group-focus/draggable:opacity-100 sm:opacity-0"
/>
</Button>
<div ref={popoverRef}>
<ForwardedIconComponent
name="GripVertical"
className="h-4 w-4 shrink-0 text-muted-foreground group-hover/draggable:text-primary"
/>
<SelectTrigger tabIndex={-1}></SelectTrigger>
<SelectContent
position="popper"
side="bottom"
sideOffset={-25}
style={{
position: "absolute",
left: cursorPos.x,
top: cursorPos.y,
}}
>
<SelectItem value={"download"}>
<div className="flex">
<IconComponent
name="Download"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Download{" "}
</div>{" "}
</SelectItem>
{!official && (
<SelectItem value={"delete"}>
draggable={!error}
style={{
borderLeftColor: color,
}}
onDragStart={onDragStart}
onDragEnd={() => {
document.body.removeChild(
document.getElementsByClassName("cursor-grabbing")[0],
);
}}
>
<ForwardedIconComponent
name={icon}
className="h-5 w-5 shrink-0"
/>
<div className="flex flex-1 items-center overflow-hidden">
<ShadTooltip content={display_name} styleClasses="z-50">
<span className="truncate text-sm font-semibold">
{display_name}
</span>
</ShadTooltip>
{beta && (
<Badge
variant="pinkStatic"
size="xq"
className="ml-1.5 shrink-0"
>
BETA
</Badge>
)}
{legacy && (
<Badge
variant="secondaryStatic"
size="xq"
className="ml-1.5 shrink-0"
>
LEGACY
</Badge>
)}
</div>
<div className="flex shrink-0 items-center gap-1">
{!disabled && (
<Button
variant="ghost"
size="icon"
tabIndex={-1}
className="text-primary"
onClick={() => addComponent(apiClass, itemName)}
>
<ForwardedIconComponent
name="Plus"
className="h-4 w-4 shrink-0 transition-all group-hover/draggable:opacity-100 group-focus/draggable:opacity-100 sm:opacity-0"
/>
</Button>
)}
<div ref={popoverRef}>
<ForwardedIconComponent
name="GripVertical"
className="h-4 w-4 shrink-0 text-muted-foreground group-hover/draggable:text-primary"
/>
<SelectTrigger tabIndex={-1}></SelectTrigger>
<SelectContent
position="popper"
side="bottom"
sideOffset={-25}
style={{
position: "absolute",
left: cursorPos.x,
top: cursorPos.y,
}}
>
<SelectItem value={"download"}>
<div className="flex">
<IconComponent
name="Trash2"
name="Download"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Delete{" "}
Download{" "}
</div>{" "}
</SelectItem>
)}
</SelectContent>
{!official && (
<SelectItem value={"delete"}>
<div className="flex">
<IconComponent
name="Trash2"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Delete{" "}
</div>{" "}
</SelectItem>
)}
</SelectContent>
</div>
</div>
</div>
</div>
</div>
</ShadTooltip>
</Select>
);
},

View file

@ -30,6 +30,7 @@ import { Switch } from "@/components/ui/switch";
import { CustomLink } from "@/customization/components/custom-link";
import { useAddComponent } from "@/hooks/useAddComponent";
import { useStoreStore } from "@/stores/storeStore";
import { checkChatInput } from "@/utils/reactflowUtils";
import {
nodeColors,
SIDEBAR_BUNDLES,
@ -97,6 +98,7 @@ export function FlowSidebarComponent() {
(category) => Object.keys(category).length > 0,
);
}, [dataFilter]);
const [sortedCategories, setSortedCategories] = useState<string[]>([]);
useEffect(() => {
filterComponents();
@ -128,6 +130,7 @@ export function FlowSidebarComponent() {
// Apply search filter
if (search && fuse) {
const results = fuse.search(search);
setSortedCategories(results.map((result) => result.item.category));
filteredData = Object.fromEntries(
Object.entries(data).map(([category, items]) => {
const categoryResults = results.filter(
@ -303,12 +306,21 @@ export function FlowSidebarComponent() {
dataFilter[item.name] && Object.keys(dataFilter[item.name]).length > 0,
);
const hasCategoryItems = categories.some(
(item) =>
dataFilter[item.name] && Object.keys(dataFilter[item.name]).length > 0,
);
function handleClearSearch() {
setSearch("");
setFilterData(data);
setOpenCategories([]);
}
const nodes = useFlowStore((state) => state.nodes);
const chatInputAdded = checkChatInput(nodes);
return (
<Sidebar collapsible="offcanvas" data-testid="shad-sidebar">
<SidebarHeader className="flex w-full flex-col gap-4 p-4 pb-1">
@ -339,12 +351,11 @@ export function FlowSidebarComponent() {
<div className="flex flex-col gap-7 border-b pb-7 pt-5">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<span className="text-sm font-medium">
Show{" "}
<Badge variant="pinkStatic" size="sq">
<span className="flex gap-2 text-sm font-medium">
Show
<Badge variant="pinkStatic" size="xq">
BETA
</Badge>{" "}
Components
</Badge>
</span>
</div>
<Switch
@ -355,12 +366,11 @@ export function FlowSidebarComponent() {
</div>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<span className="text-sm font-medium">
Show{" "}
<Badge variant="secondaryStatic" size="sq">
<span className="flex gap-2 text-sm font-medium">
Show
<Badge variant="secondaryStatic" size="xq">
LEGACY
</Badge>{" "}
Components
</Badge>
</span>
</div>
<Switch
@ -393,8 +403,7 @@ export function FlowSidebarComponent() {
Type{" "}
<span>
<ShortcutDisplay sidebar shortcut="/" />
</span>{" "}
to search components
</span>
</div>
)}
</div>
@ -402,6 +411,7 @@ export function FlowSidebarComponent() {
<SidebarFilterComponent
isInput={!!filterType.source}
type={filterType.type}
color={filterType.color}
resetFilters={() => {
setFilterEdge([]);
setFilterData(data);
@ -409,19 +419,186 @@ export function FlowSidebarComponent() {
/>
)}
</SidebarHeader>
<SidebarContent className="p-2">
<SidebarContent>
{hasResults ? (
<>
<SidebarGroup>
<SidebarGroupContent>
<SidebarMenu>
{!data
? Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton />
</SidebarMenuItem>
))
: categories.map(
{hasCategoryItems && (
<SidebarGroup className="p-3">
<SidebarGroupContent>
<SidebarMenu>
{!data
? Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton />
</SidebarMenuItem>
))
: categories
.toSorted(
(a, b) =>
(search !== ""
? sortedCategories
: categories
).findIndex((value) => value === a.name) -
(search !== ""
? sortedCategories
: categories
).findIndex((value) => value === b.name),
)
.map(
(item) =>
dataFilter[item.name] &&
Object.keys(dataFilter[item.name]).length > 0 && (
<Disclosure
key={item.name}
open={openCategories.includes(item.name)}
onOpenChange={(isOpen) => {
setOpenCategories((prev) =>
isOpen
? [...prev, item.name]
: prev.filter(
(cat) => cat !== item.name,
),
);
}}
>
<SidebarMenuItem>
<DisclosureTrigger className="group/collapsible">
<SidebarMenuButton asChild>
<div
data-testid={`disclosure-${item.display_name.toLocaleLowerCase()}`}
tabIndex={0}
onKeyDown={(e) =>
handleKeyDown(e, item.name)
}
className="flex cursor-pointer items-center gap-2"
>
<ForwardedIconComponent
name={item.icon}
className="h-4 w-4 group-aria-expanded/collapsible:text-accent-pink-foreground"
/>
<span className="flex-1 group-aria-expanded/collapsible:font-semibold">
{item.display_name}
</span>
<ForwardedIconComponent
name="ChevronRight"
className="-mr-1 h-4 w-4 text-muted-foreground transition-all group-aria-expanded/collapsible:rotate-90"
/>
</div>
</SidebarMenuButton>
</DisclosureTrigger>
<DisclosureContent>
<div className="flex flex-col gap-1 py-2">
{Object.keys(dataFilter[item.name])
.sort((a, b) =>
sensitiveSort(
dataFilter[item.name][a]
.display_name,
dataFilter[item.name][b]
.display_name,
),
)
.map((SBItemName: string, idx) => (
<ShadTooltip
content={
dataFilter[item.name][
SBItemName
].display_name
}
side="right"
key={idx}
>
<SidebarDraggableComponent
sectionName={
item.name as string
}
apiClass={
dataFilter[item.name][
SBItemName
]
}
icon={
dataFilter[item.name][
SBItemName
].icon ??
item.icon ??
"Unknown"
}
key={idx}
onDragStart={(event) =>
onDragStart(event, {
type: removeCountFromString(
SBItemName,
),
node: dataFilter[item.name][
SBItemName
],
})
}
color={nodeColors[item.name]}
itemName={SBItemName}
error={
!!dataFilter[item.name][
SBItemName
].error
}
display_name={
dataFilter[item.name][
SBItemName
].display_name
}
official={
dataFilter[item.name][
SBItemName
].official === false
? false
: true
}
beta={
dataFilter[item.name][
SBItemName
].beta ?? false
}
legacy={
dataFilter[item.name][
SBItemName
].legacy ?? false
}
disabled={
SBItemName === "ChatInput" &&
chatInputAdded
}
disabledTooltip="Chat input already added"
/>
</ShadTooltip>
))}
</div>
</DisclosureContent>
</SidebarMenuItem>
</Disclosure>
),
)}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
)}
{hasBundleItems && (
<SidebarGroup className="p-3">
<SidebarGroupLabel>Bundles</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{bundles
.toSorted(
(a, b) =>
(search !== ""
? sortedCategories
: bundles
).findIndex((value) => value === a.name) -
(search !== ""
? sortedCategories
: bundles
).findIndex((value) => value === b.name),
)
.map(
(item) =>
dataFilter[item.name] &&
Object.keys(dataFilter[item.name]).length > 0 && (
@ -440,7 +617,6 @@ export function FlowSidebarComponent() {
<DisclosureTrigger className="group/collapsible">
<SidebarMenuButton asChild>
<div
data-testid={`disclosure-${item.display_name.toLocaleLowerCase()}`}
tabIndex={0}
onKeyDown={(e) =>
handleKeyDown(e, item.name)
@ -449,14 +625,14 @@ export function FlowSidebarComponent() {
>
<ForwardedIconComponent
name={item.icon}
className="group-aria-expanded/collapsible:x-gradient h-4 w-4"
className="h-4 w-4 text-muted-foreground group-aria-expanded/collapsible:text-primary"
/>
<span className="group-aria-expanded/collapsible:font-semibold">
<span className="flex-1 group-aria-expanded/collapsible:font-semibold">
{item.display_name}
</span>
<ForwardedIconComponent
name="ChevronRight"
className="h-4 w-4 text-muted-foreground transition-all group-aria-expanded/collapsible:rotate-90"
className="-mr-1 h-4 w-4 text-muted-foreground transition-all group-aria-expanded/collapsible:rotate-90"
/>
</div>
</SidebarMenuButton>
@ -526,6 +702,11 @@ export function FlowSidebarComponent() {
dataFilter[item.name][SBItemName]
.legacy ?? false
}
disabled={
SBItemName === "ChatInput" &&
chatInputAdded
}
disabledTooltip="Chat input already added"
/>
</ShadTooltip>
))}
@ -535,126 +716,6 @@ export function FlowSidebarComponent() {
</Disclosure>
),
)}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
{hasBundleItems && (
<SidebarGroup>
<SidebarGroupLabel>Bundles</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{bundles.map(
(item) =>
dataFilter[item.name] &&
Object.keys(dataFilter[item.name]).length > 0 && (
<Disclosure
key={item.name}
open={openCategories.includes(item.name)}
onOpenChange={(isOpen) => {
setOpenCategories((prev) =>
isOpen
? [...prev, item.name]
: prev.filter((cat) => cat !== item.name),
);
}}
>
<SidebarMenuItem>
<DisclosureTrigger className="group/collapsible">
<SidebarMenuButton asChild>
<div
tabIndex={0}
onKeyDown={(e) =>
handleKeyDown(e, item.name)
}
className="flex cursor-pointer items-center gap-2"
>
<ForwardedIconComponent
name={item.icon}
className="h-4 w-4 text-muted-foreground group-aria-expanded/collapsible:text-primary"
/>
<span className="group-aria-expanded/collapsible:font-semibold">
{item.display_name}
</span>
<ForwardedIconComponent
name="ChevronRight"
className="h-4 w-4 text-muted-foreground transition-all group-aria-expanded/collapsible:rotate-90"
/>
</div>
</SidebarMenuButton>
</DisclosureTrigger>
<DisclosureContent>
<div className="flex flex-col gap-1 py-2">
{Object.keys(dataFilter[item.name])
.sort((a, b) =>
sensitiveSort(
dataFilter[item.name][a].display_name,
dataFilter[item.name][b].display_name,
),
)
.map((SBItemName: string, idx) => (
<ShadTooltip
content={
dataFilter[item.name][SBItemName]
.display_name
}
side="right"
key={idx}
>
<SidebarDraggableComponent
sectionName={item.name as string}
apiClass={
dataFilter[item.name][SBItemName]
}
icon={
dataFilter[item.name][SBItemName]
.icon ??
item.icon ??
"Unknown"
}
key={idx}
onDragStart={(event) =>
onDragStart(event, {
type: removeCountFromString(
SBItemName,
),
node: dataFilter[item.name][
SBItemName
],
})
}
color={nodeColors[item.name]}
itemName={SBItemName}
error={
!!dataFilter[item.name][SBItemName]
.error
}
display_name={
dataFilter[item.name][SBItemName]
.display_name
}
official={
dataFilter[item.name][SBItemName]
.official === false
? false
: true
}
beta={
dataFilter[item.name][SBItemName]
.beta ?? false
}
legacy={
dataFilter[item.name][SBItemName]
.legacy ?? false
}
/>
</ShadTooltip>
))}
</div>
</DisclosureContent>
</SidebarMenuItem>
</Disclosure>
),
)}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>

View file

@ -18,7 +18,7 @@ export default function ShortcutDisplay({
{name && <span> {name} </span>}
<span
className={cn(
"flex h-5 items-center rounded-sm bg-muted px-1.5 text-lg text-muted-foreground",
"flex h-[16px] w-[16px] items-center justify-center rounded-sm bg-muted text-muted-foreground",
name && "ml-3",
)}
>

View file

@ -167,7 +167,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
<div className="flow-page-positioning">
{currentFlow && (
<div className="flex h-full overflow-hidden">
<SidebarProvider width="19rem" defaultOpen={!isMobile}>
<SidebarProvider width="17.5rem" defaultOpen={!isMobile}>
{!view && <FlowSidebarComponent />}
<main className="flex flex-1">
<div className="h-full w-full">

View file

@ -273,11 +273,21 @@ pre {
}
[type="search"]:not(:placeholder-shown)::-webkit-search-cancel-button {
-webkit-appearance: none;
background-color: hsl(var(--primary));
opacity: 1 !important;
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23777'><path d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/></svg>");
background-size: 20px 20px;
height: 20px;
width: 20px;
}
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
height: 16px;
width: 16px;
margin-left: 0.4em;
position: relative;
right: -4px;
mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'><path d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/></svg>");
background-color: hsl(var(--foreground)) !important;
background-image: none;
cursor: pointer;
}
.react-flow__background pattern circle {
fill: hsl(var(--canvas-dot)) !important;
}

View file

@ -32,18 +32,17 @@
--secondary-hover: 240 6% 90%; /* hsl(240, 6%, 90%) */
--placeholder-foreground: 240 5% 65%; /* hsl(240, 5%, 65%) */
--canvas: 240 5% 96%; /* hsl(240, 5%, 96%) */
--canvas-dot: 240 5% 65%; /* hsl(240, 5%, 65%) */
--accent-emerald: 149 80% 90%; /* hsl(149, 80%, 90%) */
--accent-emerald-foreground: 161 41% 30%; /* hsl(161, 41%, 30%) */
--accent-emerald-hover: 152.4 76% 80.4%; /* hsl(152.4, 76%, 80.4%) */
--accent-indigo: 226 100% 94%; /* hsl(226, 100%, 94%) */
--accent-indigo-foreground: 243 75% 59%; /* hsl(243, 75%, 59%) */
--accent-pink: 326 78% 95%; /* hsl(326, 78%, 95%) */
--accent-pink-foreground: 333 71% 51%; /* hsl(333, 71%, 51%) */
--note-amber: 48 97% 77%; /* hsl(48, 97%, 77%) */
--tooltip: 0 0% 0%; /* hsl(0, 0%, 0%) */
--tooltip-foreground: 0 0% 100%; /* hsl(0, 0%, 100%) */
--canvas-dark: 240 6% 10%; /* hsl(240, 6%, 10%) */
--node-selected: 243 75% 59%;
--round-btn-shadow: #00000063;
--ice: #31a3cc;
@ -89,28 +88,22 @@
--status-blue: #2563eb;
--status-gray: #6b7280;
--connection: #555;
--note-default: #f1f5f9;
--note-indigo: #e0e7ff;
--note-emerald: #d1fae5;
--note-red: #fee2e2;
--note-amber: 48 96.6% 76.7%; /* hsl(48, 96.6%, 76.7%) */
--note-neutral: 240 5.9% 90%; /* hsl(240, 5.9%, 90%) */
--note-rose: 352.7 96.1% 90%; /* hsl(352.7, 96.1%, 90%) */
--note-blue: 213.3 96.9% 87.3%; /* hsl(213.3, 96.9%, 87.3%) */
--note-lime: 80.9 88.5% 79.6%; /* hsl(80.9, 88.5%, 79.6%) */
--error-red: 0, 86%, 97%; /*hsla(0, 86%, 97%)*/
--error-red-border: 0, 96%, 89%; /*hsla(0,96%,89%)*/
--note-default-opacity: #f1f5f980;
--note-indigo-opacity: #312e8180;
--note-emerald-opacity: #064e3b80;
--note-amber-opacity: #78350f80;
--note-red-opacity: #7f1d1d80;
--code-background: var(--canvas);
--emerald-success: 160.1 84.1% 39.4%;
--accent-emerald-foreground: 161.4 93.5% 30.4%;
--placeholder: 240 5% 64.9%;
--hard-zinc: 240 5.2% 33.9%;
--smooth-red: 0 93.3% 94.1%;
--radius: 0.5rem;
--accent-pink: 327.3 73.3% 97.1%;
--accent-pink-foreground: 333.3 71.4% 50.6%;
--inner-yellow: 40.6 96.1% 40.4%;
--inner-yellow-foreground: 50.4 97.8% 63.5%;
--inner-yellow-muted-foreground: 35.5 91.7% 32.9%;
@ -180,30 +173,25 @@
--secondary-hover: 240 4% 16%; /* hsl(240, 4%, 16%) */
--placeholder-foreground: 240 4% 46%; /* hsl(240, 4%, 46%) */
--canvas: 0 0% 0%; /* hsl(0, 0%, 0%) */
--canvas-dot: 240 5% 34%; /* hsl(240, 5%, 34%) */
--accent-emerald: 164 86% 16%; /* hsl(164, 86%, 16%) */
--accent-emerald-foreground: 158 64% 52%; /* hsl(158, 64%, 52%) */
--accent-emerald-hover: 163.1 88.1% 19.8%; /* hsl(163.1, 88.1%, 19.8%) */
--accent-indigo: 242 25% 34%; /* hsl(242, 25%, 34%) */
--accent-indigo-foreground: 234 89% 74%; /* hsl(234, 89%, 74%) */
--accent-pink: 336 30% 30%; /* hsl(336, 30%, 30%) */
--accent-pink: 336 69% 30%; /* hsl(336, 69%, 30%) */
--accent-pink-foreground: 329 86% 70%; /* hsl(329, 86%, 70%) */
--note-amber: 46 97% 65%; /* hsl(46, 97%, 65%) */
--tooltip: 0 0% 100%; /* hsl(0, 0%, 100%) */
--tooltip-foreground: 0 0% 0%; /* hsl(0, 0%, 0%) */
--error-red: 0, 75%, 15%; /*hsla(0, 75%, 15%)*/
--error-red-border: 0, 70%, 35%; /*hsla(0,70%,35%)*/
--note-default: #0f172a;
--note-indigo: #312e81;
--note-emerald: #064e3b;
--note-red: #7f1d1d;
--note-placeholder: 216 12% 84%; /* hsl(216 12% 84%) */
--note-default-opacity: #0f172a80;
--note-indigo-opacity: #312e8180;
--note-emerald-opacity: #064e3b80;
--note-amber-opacity: #78350f80;
--note-red-opacity: #7f1d1d80;
--note-amber: 45.9 96.7% 64.5%; /* hsl(45.9, 96.7%, 64.5%) */
--note-neutral: 240 4.9% 83.9%; /* hsl(240, 4.9%, 83.9%) */
--note-rose: 352.6 95.7% 81.8%; /* hsl(352.6, 95.7%, 81.8%) */
--note-blue: 211.7 96.4% 78.4%; /* hsl(211.7, 96.4%, 78.4%) */
--note-lime: 82 84.5% 67.1%; /* hsl(82, 84.5%, 67.1%) */
--node-selected: 234 89% 74%;
@ -234,8 +222,6 @@
--component-icon: #c35f85;
--flow-icon: #2467e4;
/* Colors that are shared in dark and light mode */
--blur-shared: #151923d2;
--build-trigger: #dc735b;
--chat-trigger: #5c8be1;
--chat-trigger-disabled: #2d3b54;
@ -262,14 +248,10 @@
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
--emerald-success: 160.1 84.1% 39.4%;
--placeholder: 240 5% 64.9%;
--hard-zinc: 240 5.2% 33.9%;
--smooth-red: 0 93.3% 94.1%;
--accent-pink: 335.9 69% 30.4%;
--accent-pink-foreground: 330.4 81.2% 60.4%;
--inner-yellow: 47.9 95.8% 53.1%;
--inner-yellow-foreground: 35.5 91.7% 32.9%;
--inner-yellow-muted-foreground: 50.4 97.8% 63.5%;

View file

@ -69,11 +69,9 @@ const config = {
"build-trigger": "var(--build-trigger)",
"chat-trigger": "var(--chat-trigger)",
"chat-trigger-disabled": "var(--chat-trigger-disabled)",
"blur-shared": "var(--blur-shared)",
"dark-blue": "var(--dark-blue)",
"dark-gray": "var(--dark-gray)",
"dark-red": "var(--dark-red)",
"note-placeholder": "var(--note-placeholder)",
error: {
DEFAULT: "var(--error)",
background: "var(--error-background)",
@ -97,7 +95,11 @@ const config = {
"medium-high-indigo": "var(--medium-high-indigo)",
"medium-indigo": "var(--medium-indigo)",
"medium-low-gray": "var(--medium-low-gray)",
"note-amber": "var(--note-amber)",
"note-amber": "hsl(var(--note-amber))",
"note-neutral": "hsl(var(--note-neutral))",
"note-rose": "hsl(var(--note-rose))",
"note-blue": "hsl(var(--note-blue))",
"note-lime": "hsl(var(--note-lime))",
"status-green": "var(--status-green)",
"status-red": "var(--status-red)",
"status-yellow": "var(--status-yellow)",
@ -120,7 +122,7 @@ const config = {
"code-background": "hsl(var(--code-background))",
canvas: {
DEFAULT: "hsl(var(--canvas))",
dark: "hsl(var(--canvas-dark))",
dot: "hsl(var(--canvas-dot))",
},
ice: "var(--ice)",
selected: "var(--selected)",
@ -128,13 +130,15 @@ const config = {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
"error-red":"hsl(var(--error-red))",
"error-red-border":"hsl(var(--error-red-border))",
"error-red": "hsl(var(--error-red))",
"error-red-border": "hsl(var(--error-red-border))",
"node-selected": "hsl(var(--node-selected))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
"emerald-success": "hsl(var(--emerald-success))",
"accent-emerald-foreground": "hsl(var(--accent-emerald-foreground))",
"accent-emerald": {
DEFAULT: "hsl(var(--accent-emerald))",
foreground: "hsl(var(--accent-emerald-foreground))",
},
"emerald-smooth": "hsl(var(--emaral-smooth))",
"emerald-hard": "hsl(var(--emeral-hard))",
placeholder: "hsl(var(--placeholder))",
@ -166,6 +170,7 @@ const config = {
"accent-emerald": {
DEFAULT: "hsl(var(--accent-emerald))",
foreground: "hsl(var(--accent-emerald-foreground))",
hover: "hsl(var(--accent-emerald-hover))",
},
"accent-indigo": {
DEFAULT: "hsl(var(--accent-indigo))",
@ -377,34 +382,34 @@ const config = {
tailwindcssTypography,
tailwindcssDottedBackground,
plugin(function ({ addUtilities, theme, e }) {
const colors = theme('colors');
const colors = theme("colors");
const generateUtilities = (colors, prefix = '') => {
const generateUtilities = (colors, prefix = "") => {
return Object.keys(colors).reduce((acc, colorName) => {
const colorValue = colors[colorName];
const className = prefix ? `${prefix}-${e(colorName)}` : e(colorName);
if (typeof colorValue === 'string') {
if (typeof colorValue === "string") {
acc[`.truncate-${className}`] = {
position: 'relative',
overflow: 'hidden',
'&::after': {
position: "relative",
overflow: "hidden",
"&::after": {
content: '""',
position: 'absolute',
inset: '0 0 0 0',
position: "absolute",
inset: "0 0 0 0",
background: `linear-gradient(to right, transparent, 75%, ${colorValue})`,
},
};
} else if (typeof colorValue === 'object') {
} else if (typeof colorValue === "object") {
// Use the DEFAULT value for the base class if it exists
if (colorValue.DEFAULT) {
acc[`.truncate-${className}`] = {
position: 'relative',
overflow: 'hidden',
'&::after': {
position: "relative",
overflow: "hidden",
"&::after": {
content: '""',
position: 'absolute',
inset: '0 0 0 0',
position: "absolute",
inset: "0 0 0 0",
background: `linear-gradient(to right, transparent, ${colorValue.DEFAULT})`,
},
};
@ -419,7 +424,7 @@ const config = {
const newUtilities = generateUtilities(colors);
addUtilities(newUtilities, ['responsive', 'hover']);
addUtilities(newUtilities, ["responsive", "hover"]);
}),
plugin(({ addVariant }) => {
addVariant("group-increment-hover", ":merge(.group-increment):hover &");

View file

@ -35,10 +35,7 @@ test.describe("group node test", () => {
await page.getByRole("button", { name: "Group" }).click();
await page.getByTestId("title-Group").dblclick();
await page
.getByTestId("popover-anchor-input-title-Group")
.first()
.fill("test");
await page.getByTestId("input-title-Group").first().fill("test");
await page.getByTestId("icon-Ungroup").first().click();
await page.keyboard.press("Control+g");
await page.getByTestId("title-OpenAI").isVisible();

View file

@ -105,7 +105,10 @@ The future of AI is both exciting and uncertain. As the technology continues to
let hasStyles = await element?.evaluate((el) => {
const style = window.getComputedStyle(el);
return style.backgroundColor === "rgb(241, 245, 249)";
return (
style.backgroundColor === "rgb(252, 211, 77)" ||
style.backgroundColor === "rgb(253, 230, 138)"
);
});
expect(hasStyles).toBe(true);
@ -113,7 +116,7 @@ The future of AI is both exciting and uncertain. As the technology continues to
await page.getByTestId("color_picker").click();
await page.getByTestId("color_picker_button_red").click();
await page.getByTestId("color_picker_button_rose").click();
await page.waitForTimeout(1000);
await page.getByTestId("note_node").click();
@ -122,7 +125,11 @@ The future of AI is both exciting and uncertain. As the technology continues to
hasStyles = await element?.evaluate((el) => {
const style = window.getComputedStyle(el);
return style.backgroundColor === "rgb(254, 226, 226)";
return (
style.backgroundColor === "rgb(253, 164, 175)" ||
style.backgroundColor === "rgb(254, 205, 211)"
);
});
expect(hasStyles).toBe(true);