Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve notes design #3938

Merged
merged 26 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
93408fb
fix notes style
Yukiyukiyeah Sep 26, 2024
5e10bb8
fix notes style
Yukiyukiyeah Sep 26, 2024
efe5997
adding ghost node
Yukiyukiyeah Sep 26, 2024
0405c91
[autofix.ci] apply automated fixes
autofix-ci[bot] Sep 26, 2024
098bd5d
Merge branch 'main' into fix-notes-design
Yukiyukiyeah Sep 26, 2024
97151f5
Add note
Yukiyukiyeah Sep 27, 2024
8afa7ba
Merge branch 'main' into fix-notes-design
Yukiyukiyeah Sep 27, 2024
4464a1f
Merge branch 'main' into fix-notes-design
Yukiyukiyeah Sep 30, 2024
ffe0fbb
Merge branch 'main' into fix-notes-design
ogabrielluiz Sep 30, 2024
e2546d3
Merge branch 'main' into fix-notes-design
Yukiyukiyeah Sep 30, 2024
41031d8
Merge branch 'main' into fix-notes-design
Yukiyukiyeah Sep 30, 2024
b56ffa4
change cursor position
Yukiyukiyeah Oct 1, 2024
55d6b73
Merge branch 'fix-notes-design' of ssh://github.com/Yukiyukiyeah/lang…
Yukiyukiyeah Oct 1, 2024
37f4f00
update notes related test
Yukiyukiyeah Oct 1, 2024
a0d3d7c
Merge branch 'main' into fix-notes-design
Yukiyukiyeah Oct 1, 2024
46d005a
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 1, 2024
241e3ee
Merge branch 'main' into fix-notes-design
ogabrielluiz Oct 1, 2024
954d529
Merge branch 'main' into fix-notes-design
ogabrielluiz Oct 1, 2024
2cc8742
Merge branch 'main' into fix-notes-design
Yukiyukiyeah Oct 1, 2024
42f0ba7
adjust shadow block width
Yukiyukiyeah Oct 1, 2024
fad57df
Merge branch 'fix-notes-design' of ssh://github.com/Yukiyukiyeah/lang…
Yukiyukiyeah Oct 1, 2024
6c92008
move cursor to middle:
Yukiyukiyeah Oct 1, 2024
9121f1e
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 1, 2024
b30bc59
Merge branch 'main' into fix-notes-design
Yukiyukiyeah Oct 1, 2024
ca3bdb7
fix padding
Yukiyukiyeah Oct 1, 2024
8d69bd2
Merge branch 'fix-notes-design' of ssh://github.com/Yukiyukiyeah/lang…
Yukiyukiyeah Oct 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export default function NodeDescription({
) : (
<Markdown
className={cn(
"markdown prose flex h-full w-full flex-col text-primary word-break-break-word dark:prose-invert",
"markdown prose flex h-full w-full flex-col text-primary word-break-break-word note-node-markdown dark:prose-invert",
mdClassName,
)}
>
Expand Down
19 changes: 1 addition & 18 deletions src/frontend/src/CustomNodes/NoteNode/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import { noteDataType } from "@/types/flow";
import { cn } from "@/utils/utils";
import { useEffect, useMemo, useRef, useState } from "react";
import { NodeResizer, NodeToolbar } from "reactflow";
import IconComponent from "../../components/genericIconComponent";
import NodeDescription from "../GenericNode/components/NodeDescription";
import NodeName from "../GenericNode/components/NodeName";
import NoteToolbarComponent from "./NoteToolbarComponent";
function NoteNode({
data,
Expand Down Expand Up @@ -67,25 +65,10 @@ function NoteNode({
}}
ref={nodeDiv}
className={cn(
"flex h-full w-full flex-col gap-3 rounded-md border border-b p-5 transition-all",
"flex h-full w-full flex-col gap-3 border border-b p-3 transition-all",
selected ? "" : "-z-50 shadow-sm",
)}
>
<div className="flex h-fit w-full items-center align-middle">
<div className="flex w-full gap-2">
<div data-testid="note_icon">
<IconComponent name="SquarePen" className="min-w-fit" />
</div>

<div className="w-11/12">
<NodeName
nodeId={data.id}
selected={selected}
display_name={data.node?.display_name || "Note"}
/>
</div>
</div>
</div>
<div
style={{
width: size.width,
Expand Down
10 changes: 10 additions & 0 deletions src/frontend/src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -910,10 +910,20 @@ 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)",
};

export const maxSizeFilesInBytes = 10 * 1024 * 1024; // 10MB in bytes
export const MAX_TEXT_LENGTH = 99999;
111 changes: 72 additions & 39 deletions src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import NoteNode from "@/CustomNodes/NoteNode";
import IconComponent from "@/components/genericIconComponent";
import LoadingComponent from "@/components/loadingComponent";
import ShadTooltip from "@/components/shadTooltipComponent";
import {
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";
import useAutoSaveFlow from "@/hooks/flows/use-autosave-flow";
Expand Down Expand Up @@ -109,6 +114,8 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
useState<OnSelectionChangeParams | null>(null);
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);

const [isAddingNote, setIsAddingNote] = useState(false);

function handleGroupNode() {
takeSnapshot();
if (validateSelection(lastSelection!, edges).length === 0) {
Expand Down Expand Up @@ -442,9 +449,60 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
[],
);

const onPaneClick = useCallback(() => {
setFilterEdge([]);
}, []);
const onPaneClick = useCallback(
(event: React.MouseEvent) => {
setFilterEdge([]);
if (isAddingNote) {
const shadowBox = document.getElementById("shadow-box");
if (shadowBox) {
shadowBox.style.display = "none";
}
const position = reactFlowInstance?.screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
const data = {
node: {
description: "",
display_name: "",
documentation: "",
template: {},
},
type: "note",
};
const newId = getNodeId(data.type);

const newNode: NodeType = {
id: newId,
type: "noteNode",
position: position || { x: 0, y: 0 },
data: {
...data,
id: newId,
},
};
setNodes((nds) => nds.concat(newNode));
setIsAddingNote(false);
}
},
[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 + 1}px`;
shadowBox.style.top = `${event.clientY + 1}px`;
}
}
},
[isAddingNote],
);

const zoomLevel = reactFlowInstance?.getZoom();

return (
<div className="h-full w-full" ref={reactFlowWrapper}>
Expand Down Expand Up @@ -483,49 +541,15 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
panActivationKeyCode={""}
proOptions={{ hideAttribution: true }}
onPaneClick={onPaneClick}
onPaneMouseMove={onPaneMouseMove}
>
<Background className="" />
{!view && (
<Controls className="fill-foreground stroke-foreground text-primary [&>button]:border-b-border [&>button]:bg-muted hover:[&>button]:bg-border">
<ControlButton
data-testid="add_note"
onClick={() => {
const wrapper = reactFlowWrapper.current!;
const viewport = reactFlowInstance?.getViewport();
const x = wrapper.getBoundingClientRect().width / 2;
const y = wrapper.getBoundingClientRect().height / 2;
const nodePosition =
reactFlowInstance?.screenToFlowPosition({ x, y })!;

const data = {
node: {
description: "",
display_name: "",
documentation: "",
template: {},
},
type: "note",
};
const newId = getNodeId(data.type);

const newNode: NodeType = {
id: newId,
type: "noteNode",
position: { x: 0, y: 0 },
data: {
...data,
id: newId,
},
};
paste(
{ nodes: [newNode], edges: [] },
{
x: nodePosition.x,
y: nodePosition?.y,
paneX: wrapper.getBoundingClientRect().x,
paneY: wrapper.getBoundingClientRect().y,
},
);
setIsAddingNote(true);
}}
className="postion react-flow__controls absolute -top-10"
>
Expand All @@ -550,6 +574,15 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
}}
/>
</ReactFlow>
<div
id="shadow-box"
style={{
position: "absolute",
width: `${NOTE_NODE_MIN_WIDTH * (zoomLevel || 1)}px`,
height: `${NOTE_NODE_MIN_HEIGHT * (zoomLevel || 1)}px`,
backgroundColor: `${SHADOW_COLOR_OPTIONS[Object.keys(SHADOW_COLOR_OPTIONS)[0]]}`,
}}
></div>
</div>
) : (
<div className="flex h-full w-full items-center justify-center">
Expand Down
14 changes: 14 additions & 0 deletions src/frontend/src/style/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,33 @@
--status-blue: #2563eb;
--status-gray: #6b7280;
--connection: #555;
--note-default: #f1f5f9;
--note-indigo: #e0e7ff;
--note-emerald: #d1fae5;
--note-amber: #fef3c7;
--note-red: #fee2e2;

--note-default-opacity: #f1f5f980;
--note-indigo-opacity: #312e8180;
--note-emerald-opacity: #064e3b80;
--note-amber-opacity: #78350f80;
--note-red-opacity: #7f1d1d80;
}

.dark {
--note-default: #0f172a;
--note-indigo: #312e81;
--note-emerald: #064e3b;
--note-amber: #78350f;
--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;

--node-selected: 234 89% 74%;
--background: 224 28% 7.5%; /* hsl(224 10% 7.5%) */
--foreground: 213 31% 80%; /* hsl(213 31% 91%) */
Expand Down
12 changes: 12 additions & 0 deletions src/frontend/tailwind.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,18 @@ const config = {
outline: "none !important",
outlineOffset: "0px !important",
},
".note-node-markdown": {
lineHeight: "1",
"& ul li::marker": {
color: "black",
},
"& ol li::marker": {
color: "black",
},
"& h1, & h2, & h3, & h4, & h5, & h6, & p, & ul, & ol": {
marginBottom: "0.25rem",
},
},
});
}),
tailwindcssTypography,
Expand Down
25 changes: 12 additions & 13 deletions src/frontend/tests/extended/features/sticky-notes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,15 @@ test("user should be able to interact with sticky notes", async ({ page }) => {
control = "Meta";
}

const noteText = `
Artificial Intelligence (AI) has rapidly evolved from a speculative concept in science fiction to a transformative force reshaping industries and everyday life. The term AI encompasses a broad range of technologies, from simple algorithms designed to perform specific tasks to complex systems capable of learning and adapting independently. As AI continues to advance, its applications are becoming increasingly diverse, impacting everything from healthcare to finance, entertainment, and beyond.
const randomTitle = Math.random()
.toString(36)
.substring(7)
.padEnd(8, "x")
.substring(0, 8);

const noteText = `# ${randomTitle}

Artificial Intelligence (AI) has rapidly evolved from a speculative concept in science fiction to a transformative force reshaping industries and everyday life. The term AI encompasses a broad range of technologies, from simple algorithms designed to perform specific tasks to complex systems capable of learning and adapting independently. As AI continues to advance, its applications are becoming increasingly diverse, impacting everything from healthcare to finance, entertainment, and beyond.

At its core, AI is about creating systems that can perform tasks that would typically require human intelligence. This includes abilities such as visual perception, speech recognition, decision-making, and even language translation. The development of AI can be traced back to the mid-20th century, when pioneers like Alan Turing began exploring the idea of machines that could think. Turing's famous "Turing Test" proposed a benchmark for AI, where a machine would be considered intelligent if it could engage in a conversation with a human without being detected as a machine.

Expand All @@ -50,8 +57,6 @@ Despite its many benefits, AI also raises important ethical and societal questio
The future of AI is both exciting and uncertain. As the technology continues to advance, it will undoubtedly bring about profound changes in society. The challenge will be to harness AI's potential for good while addressing the ethical and societal issues that arise. Whether it's through smarter healthcare, more efficient transportation, or enhanced creativity, AI has the potential to reshape the world in ways we are only beginning to imagine. The journey of AI is far from over, and its impact will be felt for generations to come.
`;

const randomTitle = Math.random().toString(36).substring(7);

while (modalCount === 0) {
await page.getByText("New Project", { exact: true }).click();
await page.waitForTimeout(3000);
Expand All @@ -70,6 +75,7 @@ The future of AI is both exciting and uncertain. As the technology continues to
await page.waitForTimeout(1000);

const targetElement = await page.locator('//*[@id="react-flow-id"]');
await targetElement.click();

await page.mouse.up();
await page.mouse.down();
Expand All @@ -84,19 +90,12 @@ The future of AI is both exciting and uncertain. As the technology continues to

await page.getByTestId("note_node").click();

await page.getByTestId("title-Note").dblclick();
await page.waitForTimeout(1000);
await page.getByTestId("popover-anchor-input-title-Note").fill(randomTitle);

await page.getByTestId("note_icon").first().dblclick();

await page.locator(".generic-node-desc").last().dblclick();
await page.getByTestId("textarea").fill(noteText);

expect(await page.getByText("2500/2500")).toBeVisible();

await page.getByTestId("note_icon").first().dblclick();

await targetElement.click();
const textMarkdown = await page.locator(".markdown").innerText();

const textLength = textMarkdown.length;
Expand All @@ -110,7 +109,7 @@ 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(224, 231, 255)";
return style.backgroundColor === "rgb(241, 245, 249)";
});
expect(hasStyles).toBe(true);

Expand Down