Skip to content
Merged

chat ui #1579

Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 0 additions & 24 deletions apps/desktop2/src/components/chat/body/empty.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,9 @@
import { Building2, CheckCircle, MessageCircle, User } from "lucide-react";

export function ChatBodyEmpty() {
return (
<div className="flex flex-col items-center justify-center h-full text-center px-8 py-12">
<img src="/assets/dynamic.gif" alt="Hyprnote" className="w-24 h-24 mb-6 mt-12" />
<p className="text-neutral-600 text-base mb-1">Ask the AI assistant about anything.</p>
<p className="text-neutral-400 text-sm mb-10">It can also do few cool stuff for you.</p>

<div className="flex flex-col gap-3 items-center w-full">
<button className="flex items-center gap-3 px-0 py-2 text-left text-neutral-700 hover:text-neutral-900 transition-colors">
<User className="w-5 h-5 flex-shrink-0" />
<span className="text-sm">Tell me more about John Jeong</span>
</button>

<button className="flex items-center gap-3 px-0 py-2 text-left text-neutral-700 hover:text-neutral-900 transition-colors">
<Building2 className="w-5 h-5 flex-shrink-0" />
<span className="text-sm">Tell me more about Fastrepl, Inc.</span>
</button>

<button className="flex items-center gap-3 px-0 py-2 text-left text-neutral-700 hover:text-neutral-900 transition-colors">
<MessageCircle className="w-5 h-5 flex-shrink-0" />
<span className="text-sm">Summarize the last meeting</span>
</button>

<button className="flex items-center gap-3 px-0 py-2 text-left text-neutral-700 hover:text-neutral-900 transition-colors">
<CheckCircle className="w-5 h-5 flex-shrink-0" />
<span className="text-sm">Check if next steps are all done</span>
</button>
</div>
</div>
);
}
34 changes: 20 additions & 14 deletions apps/desktop2/src/components/chat/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { formatDistanceToNow } from "date-fns";
import { ChevronDown, MessageCircle, PanelRightIcon, PictureInPicture2Icon, Plus, X } from "lucide-react";
import { useState } from "react";

import { Button } from "@hypr/ui/components/ui/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "@hypr/ui/components/ui/dropdown-menu";
import { cn } from "@hypr/ui/lib/utils";
import { useShell } from "../../contexts/shell";
Expand Down Expand Up @@ -31,13 +32,13 @@ export function ChatHeader({
<div className="flex items-center">
<ChatGroups currentChatGroupId={currentChatGroupId} onSelectChat={onSelectChat} />
<ChatActionButton
icon={<Plus className="w-4 h-4" />}
icon={<Plus size={16} />}
onClick={onNewChat}
title="New chat"
/>
</div>

<div className="flex items-center gap-0.5">
<div className="flex items-center">
<ChatActionButton
icon={chat.mode === "RightPanelOpen"
? <PictureInPicture2Icon className="w-4 h-4" />
Expand All @@ -46,7 +47,7 @@ export function ChatHeader({
title="Toggle"
/>
<ChatActionButton
icon={<X className="w-4 h-4" />}
icon={<X size={16} />}
onClick={handleClose}
title="Close"
/>
Expand All @@ -65,13 +66,14 @@ function ChatActionButton({
onClick: () => void;
}) {
return (
<button
<Button
onClick={onClick}
className="p-1 text-neutral-600 hover:text-neutral-900 hover:bg-neutral-100 rounded-lg transition-all active:scale-95"
title={title}
size="icon"
variant="ghost"
>
{icon}
</button>
</Button>
);
}

Expand Down Expand Up @@ -102,7 +104,10 @@ function ChatGroups({
return (
<DropdownMenu open={isDropdownOpen} onOpenChange={setIsDropdownOpen}>
<DropdownMenuTrigger asChild>
<button className="flex items-center gap-2 hover:bg-neutral-100/60 active:bg-neutral-100 px-2 py-1.5 rounded-lg transition-all group focus:ring-2 focus:ring-neutral-400 focus:ring-offset-1">
<Button
variant="ghost"
className="flex items-center gap-2 h-auto px-2 py-1.5 group"
>
<MessageCircle className="w-3.5 h-3.5 text-neutral-400 group-hover:text-neutral-600 transition-colors" />
<h3 className="font-medium text-neutral-700 text-xs truncate">
{currentChatTitle || "Ask Hyprnote anything"}
Expand All @@ -113,7 +118,7 @@ function ChatGroups({
isDropdownOpen && "rotate-180",
])}
/>
</button>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-72 p-1.5">
<div className="space-y-0.5">
Expand Down Expand Up @@ -168,14 +173,15 @@ function ChatGroupItem({
: "";

return (
<button
<Button
variant="ghost"
onClick={() => onSelect(groupId)}
className={cn([
"w-full text-left px-2.5 py-1.5 rounded-md transition-all group",
isActive ? "bg-neutral-100 shadow-sm" : "hover:bg-neutral-50 active:bg-neutral-100",
"w-full justify-start h-auto px-2.5 py-1.5 group",
isActive ? "bg-neutral-100 shadow-sm hover:bg-neutral-100" : "hover:bg-neutral-50 active:bg-neutral-100",
])}
>
<div className="flex items-center gap-2.5">
<div className="flex items-center gap-2.5 w-full">
<div className="flex-shrink-0">
<MessageCircle
className={cn([
Expand All @@ -184,7 +190,7 @@ function ChatGroupItem({
])}
/>
</div>
<div className="flex-1 min-w-0">
<div className="flex-1 min-w-0 text-left">
<div
className={cn([
"text-sm font-medium truncate",
Expand All @@ -198,6 +204,6 @@ function ChatGroupItem({
</div>
</div>
</div>
</button>
</Button>
);
}
87 changes: 63 additions & 24 deletions apps/desktop2/src/components/chat/input.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { MicIcon, PaperclipIcon, SendIcon } from "lucide-react";
import { useCallback, useEffect, useRef } from "react";

import Editor from "@hypr/tiptap/editor";
import { Button } from "@hypr/ui/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@hypr/ui/components/ui/select";
import { cn } from "@hypr/ui/lib/utils";

import { MicIcon, PaperclipIcon, SendIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";

import { useShell } from "../../contexts/shell";

function tiptapJsonToText(json: any): string {
Expand Down Expand Up @@ -34,6 +37,7 @@ export function ChatMessageInput({
}) {
const editorRef = useRef<{ editor: any }>(null);
const { chat } = useShell();
const [selectOpen, setSelectOpen] = useState(false);

const handleSubmit = useCallback(() => {
const json = editorRef.current?.editor?.getJSON();
Expand Down Expand Up @@ -65,20 +69,16 @@ export function ChatMessageInput({

return (
<div
className={cn([chat.mode !== "RightPanelOpen" && "p-0.5"])}
className={cn([chat.mode !== "RightPanelOpen" && "p-1"])}
>
<div
className={cn([
"flex items-center gap-2 px-3 py-2 border border-neutral-200 rounded-md",
"flex flex-col border border-neutral-200 rounded-xl",
"focus-within:ring-1 focus-within:ring-blue-500 focus-within:border-blue-500",
chat.mode === "RightPanelOpen" && "rounded-t-none border-t-0",
])}
>
<button className={cn(["text-neutral-400 hover:text-neutral-600 transition-colors shrink-0"])}>
<PaperclipIcon className="size-4" />
</button>

<div className={cn(["flex-1 min-w-0"])}>
<div className="flex-1 px-2 pt-2">
<Editor
ref={editorRef}
handleChange={() => {}}
Expand All @@ -91,20 +91,59 @@ export function ChatMessageInput({
/>
</div>

<button className={cn(["text-neutral-400 hover:text-neutral-600 transition-colors shrink-0"])}>
<MicIcon className="size-4" />
</button>

<button
onClick={handleSubmit}
disabled={disabled}
className={cn([
"p-1.5 bg-neutral-100 hover:bg-neutral-200 rounded-lg transition-colors shrink-0",
"disabled:opacity-50 disabled:cursor-not-allowed",
])}
>
<SendIcon className="size-4" />
</button>
<div className="flex items-center justify-between px-2 pb-2 -mt-4">
<div className="flex items-center">
<Button
size="icon"
variant="ghost"
className="text-neutral-400 shrink-0"
>
<PaperclipIcon size={16} />
</Button>

<Select
open={selectOpen}
onOpenChange={(open) => {
setSelectOpen(open);
if (!open) {
requestAnimationFrame(() => {
editorRef.current?.editor?.commands.focus();
});
}
}}
>
<SelectTrigger className="h-8 text-xs border-0 focus:ring-0 focus:ring-offset-0 shadow-none hover:bg-accent">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="auto">Auto</SelectItem>
<SelectItem value="all-notes">All Notes</SelectItem>
<SelectItem value="all-tabs">All Tabs</SelectItem>
<SelectItem value="current-tab">Current Tab</SelectItem>
</SelectContent>
</Select>
</div>

<div className="flex items-center gap-1">
<Button
size="icon"
variant="ghost"
className="text-neutral-400"
>
<MicIcon size={16} />
</Button>

<Button
onClick={handleSubmit}
disabled={disabled}
size="icon"
variant="ghost"
className={cn(disabled && "text-neutral-400")}
>
<SendIcon size={16} />
</Button>
</div>
</div>
</div>
</div>
);
Expand Down
Loading