aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcodetorso <[email protected]>2024-07-18 23:14:48 +0530
committercodetorso <[email protected]>2024-07-18 23:14:48 +0530
commitebf5b29cc7ab22e7d70574b1b2519df924878f85 (patch)
treefd9b4bba0f067714322fa93a35fd35dc2d385315
parentsearchparams refactor (diff)
downloadsupermemory-ebf5b29cc7ab22e7d70574b1b2519df924878f85.tar.xz
supermemory-ebf5b29cc7ab22e7d70574b1b2519df924878f85.zip
improved textarea layout + home page imrpovemnts
-rw-r--r--apps/web/app/(dash)/chat/chatWindow.tsx2
-rw-r--r--apps/web/app/(dash)/chat/queryinput.tsx83
-rw-r--r--apps/web/app/(dash)/home/filterSpaces.tsx110
-rw-r--r--apps/web/app/(dash)/home/heading.tsx62
-rw-r--r--apps/web/app/(dash)/home/headingVariants.ts50
-rw-r--r--apps/web/app/(dash)/home/queryinput.tsx45
-rw-r--r--packages/ui/icons/arrowright.svg4
-rw-r--r--packages/ui/shadcn/command.tsx2
-rw-r--r--packages/ui/shadcn/divider.tsx7
9 files changed, 185 insertions, 180 deletions
diff --git a/apps/web/app/(dash)/chat/chatWindow.tsx b/apps/web/app/(dash)/chat/chatWindow.tsx
index 3bc9fec6..066e7d20 100644
--- a/apps/web/app/(dash)/chat/chatWindow.tsx
+++ b/apps/web/app/(dash)/chat/chatWindow.tsx
@@ -2,7 +2,7 @@
import { AnimatePresence } from "framer-motion";
import React, { useEffect, useRef, useState } from "react";
-import QueryInput from "../home/queryinput";
+import QueryInput from "./queryinput";
import { cn } from "@repo/ui/lib/utils";
import { motion } from "framer-motion";
import { useRouter } from "next/navigation";
diff --git a/apps/web/app/(dash)/chat/queryinput.tsx b/apps/web/app/(dash)/chat/queryinput.tsx
new file mode 100644
index 00000000..99f55986
--- /dev/null
+++ b/apps/web/app/(dash)/chat/queryinput.tsx
@@ -0,0 +1,83 @@
+"use client";
+
+import { ArrowRightIcon } from "@repo/ui/icons";
+import Image from "next/image";
+import React, { useState } from "react";
+
+function QueryInput({
+ initialSpaces,
+ initialQuery = "",
+ disabled = false,
+ className,
+ mini = false,
+ handleSubmit,
+}: {
+ initialQuery?: string;
+ initialSpaces?: {
+ id: number;
+ name: string;
+ }[];
+ disabled?: boolean;
+ className?: string;
+ mini?: boolean;
+ handleSubmit: (q: string, spaces: { id: number; name: string }[]) => void;
+}) {
+ const [q, setQ] = useState(initialQuery);
+
+ const [selectedSpaces, setSelectedSpaces] = useState<
+ { id: number; name: string }[]
+ >([]);
+
+ return (
+ <div className={`${className}`}>
+ <div
+ className={`bg-[#1F2428] overflow-hidden border-2 border-gray-700/50 shadow-md shadow-[#1d1d1dc7] rounded-3xl`}
+ >
+ {/* input and action button */}
+ <form
+ action={async () => {
+ if (q.trim().length === 0) {
+ return;
+ }
+ handleSubmit(q, selectedSpaces);
+ setQ("");
+ }}
+ className="flex gap-4 p-3"
+ >
+ <textarea
+ autoFocus
+ name="q"
+ cols={30}
+ rows={mini ? 2 : 4}
+ className="bg-transparent pt-2.5 text-lg placeholder:text-[#9B9B9B] focus:text-gray-200 duration-200 tracking-[3%] outline-none resize-none w-full p-4"
+ placeholder="Ask your second brain..."
+ onKeyDown={(e) => {
+ if (e.key === "Enter" && !e.shiftKey) {
+ e.preventDefault();
+ if (q.trim().length === 0) {
+ return;
+ }
+ handleSubmit(q, selectedSpaces);
+ setQ("");
+ }
+ }}
+ onChange={(e) => setQ(e.target.value)}
+ value={q}
+ disabled={disabled}
+ />
+
+ <button
+ type="submit"
+ disabled={disabled}
+ className="h-12 w-12 rounded-[14px] bg-border all-center shrink-0 hover:brightness-125 duration-200 outline-none focus:outline focus:outline-primary active:scale-90"
+ >
+ <Image src={ArrowRightIcon} alt="Right arrow icon" />
+ </button>
+ </form>{" "}
+ </div>
+ {/* selected sources */}
+ </div>
+ );
+}
+
+export default QueryInput;
diff --git a/apps/web/app/(dash)/home/filterSpaces.tsx b/apps/web/app/(dash)/home/filterSpaces.tsx
index 6a8ad9ec..c7d1e2eb 100644
--- a/apps/web/app/(dash)/home/filterSpaces.tsx
+++ b/apps/web/app/(dash)/home/filterSpaces.tsx
@@ -1,4 +1,5 @@
import { ChevronUpDownIcon } from "@heroicons/react/24/outline";
+import { ArrowRightIcon } from "@repo/ui/icons";
import {
Command,
CommandGroup,
@@ -7,6 +8,7 @@ import {
CommandList,
} from "@repo/ui/shadcn/command";
import { Check } from "lucide-react";
+import Image from "next/image";
import React, { useState } from "react";
type space = {
@@ -17,11 +19,11 @@ type space = {
export function FilterSpaces({
initialSpaces,
selectedSpaces,
- setSelectedSpaces
+ setSelectedSpaces,
}: {
initialSpaces: space[];
selectedSpaces: space[];
- setSelectedSpaces: React.Dispatch<React.SetStateAction<space[]>>
+ setSelectedSpaces: React.Dispatch<React.SetStateAction<space[]>>;
}) {
const [input, setInput] = useState<string>("");
@@ -35,55 +37,71 @@ export function FilterSpaces({
setSelectedSpaces((current) =>
current.some((space) => space.id === selectedSpace.id)
? current.filter((space) => space.id !== selectedSpace.id)
- : [...current, selectedSpace]
+ : [...current, selectedSpace],
);
};
return (
- <div className={`flex rounded-md overflow-hidden ${selectedSpaces.length ? "bg-[#2C3338]" : ""}`}>
- <div className="flex rounded-lg items-center">
- {selectedSpaces.map((v) => (
- <button
- key={v.id}
- onClick={() => handleSelect(v)}
- className="bg-[#3a4248] max-w-32 truncate-wor truncate whitespace-nowrap py-1 rounded-md px-2 mx-1 aria-selected:outline"
- >
- {v.name}
- </button>
- ))}
- </div>
- <Command className={`group border-0 bg-[rgb(44,51,56)] text-white outline-0 ${selectedSpaces.length ? "w-full" : "w-44"}`}>
- <div className="relative">
- <CommandInput
- placeholder={selectedSpaces.length ? "" : "Search in Spaces"}
- onKeyDown={handleKeyDown}
- className="text-white peer placeholder:text-white"
- // @ts-ignore - trust me bro it works
- onChange={(e) => setInput(e.currentTarget.value)}
- value={input}
- />
- <ChevronUpDownIcon
- className={`h-6 w-6 text-[#858B92] absolute top-1/2 right-4 -translate-y-1/2 ${selectedSpaces.length && "opacity-0"}`}
- />
+ <div className="flex p-2 px-3 w-full items-center justify-between rounded-xl overflow-hidden">
+ <div className="flex bg-[#2C3338] rounded-xl overflow-hidden pl-1">
+ <div className="flex rounded-lg items-center">
+ {selectedSpaces.map((v) => (
+ <button
+ key={v.id}
+ onClick={() => handleSelect(v)}
+ className="bg-[#3a4248] max-w-32 truncate-wor truncate whitespace-nowrap py-1 rounded-md px-2 mx-1 aria-selected:outline"
+ >
+ {v.name}
+ </button>
+ ))}
</div>
- <CommandList className="z-10 translate-y-12 translate-x-5 opacity-0 absolute group-focus-within:opacity-100 transition-opacity p-2 rounded-b-xl max-w-64 bg-[#2C3338]">
- <CommandGroup className="hidden group-focus-within:block">
- {initialSpaces.map((space) => (
- <CommandItem
- className="text-[#eaeaea] data-[disabled]:opacity-90"
- value={space.name}
- key={space.id}
- onSelect={() => handleSelect(space)}
- >
- <Check
- className={`mr-2 h-4 w-4 ${selectedSpaces.some((v) => v.id === space.id) ? "opacity-100" : "opacity-0"}`}
- />
- {space.name}
- </CommandItem>
- ))}
- </CommandGroup>
- </CommandList>
- </Command>
+ <Command
+ className={`group transition-all border-0 bg-[#2c3338] text-white outline-0 ${
+ selectedSpaces.length ? "w-5 hover:w-24 focus-within:w-20" : "w-44"
+ }`}
+ >
+ <div className="relative">
+ <CommandInput
+ placeholder={selectedSpaces.length ? "" : "Search in Spaces"}
+ onKeyDown={handleKeyDown}
+ className="text-white peer placeholder:text-white pl-2"
+ onChangeCapture={(e) => setInput(e.currentTarget.value)}
+ value={input}
+ />
+ <ChevronUpDownIcon
+ className={`h-6 w-6 text-[#858B92] pointer-events-none absolute top-1/2 -translate-y-1/2 right-2 ${
+ selectedSpaces.length && "opacity-0"
+ }`}
+ />
+ </div>
+ <CommandList className="z-10 translate-y-12 translate-x-5 opacity-0 absolute group-focus-within:opacity-100 transition-opacity p-2 rounded-lg max-w-64 bg-[#2C3338]">
+ <CommandGroup className="pointer-events-none opacity-0 group-focus-within:opacity-100 scale-50 scale-y-50 group-focus-within:scale-y-100 group-focus-within:scale-100 group-focus-within:pointer-events-auto transition-all origin-top">
+ {initialSpaces.map((space) => (
+ <CommandItem
+ className="text-[#eaeaea] data-[disabled]:opacity-90"
+ value={space.name}
+ key={space.id}
+ onSelect={() => handleSelect(space)}
+ >
+ <Check
+ className={`mr-2 h-4 w-4 ${selectedSpaces.some((v) => v.id === space.id) ? "opacity-100" : "opacity-0"}`}
+ />
+ {space.name}
+ </CommandItem>
+ ))}
+ </CommandGroup>
+ </CommandList>
+ </Command>
+ </div>
+ {/* <button
+ type="submit"
+ className="h-12 w-12 rounded-[14px] all-center shrink-0 hover:brightness-125 outline-none bg-[#369DFD1A] p-3 active:scale-90"
+ >
+ <Image src={ArrowRightIcon} alt="Right arrow icon" />
+ </button> */}
+ <button type="submit" className="rounded-lg bg-[#369DFD1A] p-3 transition-colors">
+ <Image src={ArrowRightIcon} alt="Enter" />
+ </button>
</div>
);
}
diff --git a/apps/web/app/(dash)/home/heading.tsx b/apps/web/app/(dash)/home/heading.tsx
index 1a120684..b61ac00b 100644
--- a/apps/web/app/(dash)/home/heading.tsx
+++ b/apps/web/app/(dash)/home/heading.tsx
@@ -1,48 +1,30 @@
import { useEffect, useState } from "react";
-import { headings } from "./headingVariants";
-import { motion } from "framer-motion";
+import { motion } from "framer-motion";
-const slap = {
- initial: {
- opacity: 0,
- scale: 1.1,
- },
- whileInView: { opacity: 1, scale: 1 },
- transition: {
- duration: 0.5,
- ease: "easeInOut",
- },
- viewport: { once: true },
-};
+const headings = [
+ "Unlock your digital brain",
+ "Save everything.",
+ " Connect anything.",
+ "Turn your bookmarks into insights.",
+ "The smart way to use your digital treasure.",
+];
-export function Heading() {
+export function Heading({ query = "" }: { query?: string }) {
const [showHeading, setShowHeading] = useState<number>(0);
- useEffect(()=> {
+ useEffect(() => {
setShowHeading(Math.floor(Math.random() * headings.length));
- })
+ });
return (
- <motion.h1
- {...{
- ...slap,
- transition: { ...slap.transition, delay: 0.2 },
- }}
- className="text-center mx-auto bg-[linear-gradient(180deg,_#FFF_0%,_rgba(255,_255,_255,_0.00)_202.08%)] bg-clip-text text-4xl tracking-tighter text-transparent md:text-5xl"
- >
- {headings[showHeading]!.map((v, i) => {
- return (
- <span
- key={i}
- className={
- v.type === "highlighted"
- ? "bg-gradient-to-r to-blue-200 from-zinc-300 text-transparent bg-clip-text"
- : ""
- }
- >
- {v.content}
- </span>
- );
- })}
- </motion.h1>
+ <div className="h-[3.4rem] overflow-hidden text-white text-center">
+ <motion.h1
+ animate={{ opacity: query ? 0 : 1, y: query ? "20%" : 0 }}
+ className={`text-[2.45rem] font-semibold ${
+ query ? "opacity-0 " : "opacity-100"
+ } transition-opacity`}
+ >
+ {headings[showHeading]}
+ </motion.h1>
+ </div>
);
-} \ No newline at end of file
+}
diff --git a/apps/web/app/(dash)/home/headingVariants.ts b/apps/web/app/(dash)/home/headingVariants.ts
deleted file mode 100644
index 578b87c4..00000000
--- a/apps/web/app/(dash)/home/headingVariants.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-export const headings = [
- [
- {
- type: "text",
- content: "Unlock your",
- },
- {
- type: "highlighted",
- content: " digital brain",
- },
- ],
- [
- {
- type: "text",
- content: "Save",
- },
- {
- type: "highlighted",
- content: " everything.",
- },
- {
- type: "text",
- content: " Connect",
- },
- {
- type: "highlighted",
- content: " anything.",
- },
- ],
- [
- {
- type: "text",
- content: "Turn your bookmarks into",
- },
- {
- type: "highlighted",
- content: " insights.",
- },
- ],
- [
- {
- type: "text",
- content: "The smart way to use your",
- },
- {
- type: "highlighted",
- content: " digital treasure.",
- },
- ],
-];
diff --git a/apps/web/app/(dash)/home/queryinput.tsx b/apps/web/app/(dash)/home/queryinput.tsx
index 225b5039..91f79e5f 100644
--- a/apps/web/app/(dash)/home/queryinput.tsx
+++ b/apps/web/app/(dash)/home/queryinput.tsx
@@ -1,30 +1,22 @@
"use client";
-import { ArrowRightIcon } from "@repo/ui/icons";
-import Image from "next/image";
import React, { useState } from "react";
-import Divider from "@repo/ui/shadcn/divider";
import { FilterSpaces } from "./filterSpaces";
function QueryInput({
initialSpaces,
- initialQuery = "",
- disabled = false,
className,
- mini = false,
handleSubmit,
}: {
- initialQuery?: string;
initialSpaces?: {
id: number;
name: string;
}[];
- disabled?: boolean;
className?: string;
mini?: boolean;
handleSubmit: (q: string, spaces: { id: number; name: string }[]) => void;
}) {
- const [q, setQ] = useState(initialQuery);
+ const [q, setQ] = useState("");
const [selectedSpaces, setSelectedSpaces] = useState<
{ id: number; name: string }[]
@@ -33,7 +25,7 @@ function QueryInput({
return (
<div className={`${className}`}>
<div
- className={`bg-[#1F2428] overflow-hidden border-2 border-gray-700/50 shadow-md shadow-[#1d1d1dc7] rounded-3xl`}
+ className={`bg-[#1F2428] overflow-hidden border-2 border-gray-700/50 shadow-md shadow-[#1d1d1dc7] rounded-3xl`}
>
{/* input and action button */}
<form
@@ -44,14 +36,14 @@ function QueryInput({
handleSubmit(q, selectedSpaces);
setQ("");
}}
- className="flex gap-4 p-3"
+ className=""
>
<textarea
autoFocus
name="q"
cols={30}
- rows={mini ? 2 : 4}
- className="bg-transparent pt-2.5 text-lg placeholder:text-[#9B9B9B] focus:text-gray-200 duration-200 tracking-[3%] outline-none resize-none w-full p-4"
+ rows={4}
+ className="bg-transparent pt-2.5 text-lg placeholder:text-[#9B9B9B] text-gray-200 tracking-[3%] outline-none resize-none w-full p-4"
placeholder="Ask your second brain..."
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
@@ -65,29 +57,14 @@ function QueryInput({
}}
onChange={(e) => setQ(e.target.value)}
value={q}
- disabled={disabled}
/>
-
- <button
- type="submit"
- disabled={disabled}
- className="h-12 w-12 rounded-[14px] bg-border all-center shrink-0 hover:brightness-125 duration-200 outline-none focus:outline focus:outline-primary active:scale-90"
- >
- <Image src={ArrowRightIcon} alt="Right arrow icon" />
- </button>
- </form>{" "}
- {!mini && (
- <>
- <Divider />
- <FilterSpaces
- selectedSpaces={selectedSpaces}
- setSelectedSpaces={setSelectedSpaces}
- initialSpaces={initialSpaces || []}
- />
- </>
- )}
+ <FilterSpaces
+ selectedSpaces={selectedSpaces}
+ setSelectedSpaces={setSelectedSpaces}
+ initialSpaces={initialSpaces || []}
+ />
+ </form>
</div>
- {/* selected sources */}
</div>
);
}
diff --git a/packages/ui/icons/arrowright.svg b/packages/ui/icons/arrowright.svg
index 2263251c..13d24bf5 100644
--- a/packages/ui/icons/arrowright.svg
+++ b/packages/ui/icons/arrowright.svg
@@ -1 +1,3 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 20 20"><path stroke="#989EA4" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M11.25 16.25L17.5 10M17.5 10L11.25 3.75M17.5 10H2.5"/></svg> \ No newline at end of file
+<svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M10.25 13.25L16.5 7M16.5 7L10.25 0.75M16.5 7H1.5" stroke="#369DFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/packages/ui/shadcn/command.tsx b/packages/ui/shadcn/command.tsx
index 94c4434f..9b95f4c7 100644
--- a/packages/ui/shadcn/command.tsx
+++ b/packages/ui/shadcn/command.tsx
@@ -38,7 +38,7 @@ const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className , ...props }, ref) => (
- <div className="flex items-center px-3" cmdk-input-wrapper="">
+ <div className="" cmdk-input-wrapper="">
<CommandPrimitive.Input
ref={ref}
className={cn(
diff --git a/packages/ui/shadcn/divider.tsx b/packages/ui/shadcn/divider.tsx
deleted file mode 100644
index a6dc9933..00000000
--- a/packages/ui/shadcn/divider.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import { cn } from "@repo/ui/lib/utils";
-
-function Divider({ className }: { className?: string }) {
- return <div className={cn("bg-[#2D343A] h-[1px] w-full", className)}></div>;
-}
-
-export default Divider;