mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-27 20:29:44 +02:00
simplified the new assistant text box
This commit is contained in:
parent
a0734f7013
commit
86a33994b9
2 changed files with 53 additions and 41 deletions
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
import { forwardRef, TextareaHTMLAttributes } from 'react';
|
import { forwardRef, TextareaHTMLAttributes } from 'react';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { Send } from 'lucide-react';
|
import { Send, MoreVertical } from 'lucide-react';
|
||||||
|
import { Dropdown, DropdownItem, DropdownMenu, DropdownTrigger } from '@heroui/react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
interface TextareaWithSendProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'onChange'> {
|
interface TextareaWithSendProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'onChange'> {
|
||||||
|
|
@ -11,6 +12,9 @@ interface TextareaWithSendProps extends Omit<TextareaHTMLAttributes<HTMLTextArea
|
||||||
onSubmit: () => void;
|
onSubmit: () => void;
|
||||||
isSubmitting?: boolean;
|
isSubmitting?: boolean;
|
||||||
submitDisabled?: boolean;
|
submitDisabled?: boolean;
|
||||||
|
onImportJson?: () => void;
|
||||||
|
importDisabled?: boolean;
|
||||||
|
isImporting?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
rows?: number;
|
rows?: number;
|
||||||
|
|
@ -25,6 +29,9 @@ export const TextareaWithSend = forwardRef<HTMLTextAreaElement, TextareaWithSend
|
||||||
onSubmit,
|
onSubmit,
|
||||||
isSubmitting = false,
|
isSubmitting = false,
|
||||||
submitDisabled = false,
|
submitDisabled = false,
|
||||||
|
onImportJson,
|
||||||
|
importDisabled = false,
|
||||||
|
isImporting = false,
|
||||||
placeholder,
|
placeholder,
|
||||||
className,
|
className,
|
||||||
rows = 3,
|
rows = 3,
|
||||||
|
|
@ -32,6 +39,7 @@ export const TextareaWithSend = forwardRef<HTMLTextAreaElement, TextareaWithSend
|
||||||
autoResize = false,
|
autoResize = false,
|
||||||
...props
|
...props
|
||||||
}, ref) => {
|
}, ref) => {
|
||||||
|
const hasMore = Boolean(onImportJson);
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Textarea
|
<Textarea
|
||||||
|
|
@ -39,7 +47,11 @@ export const TextareaWithSend = forwardRef<HTMLTextAreaElement, TextareaWithSend
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
className={clsx("pr-14", className)}
|
className={clsx(
|
||||||
|
// Extra right padding for kebab + send controls
|
||||||
|
hasMore ? "pr-24" : "pr-14",
|
||||||
|
className
|
||||||
|
)}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
autoResize={autoResize}
|
autoResize={autoResize}
|
||||||
|
|
@ -51,7 +63,35 @@ export const TextareaWithSend = forwardRef<HTMLTextAreaElement, TextareaWithSend
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
<div className="absolute right-3 bottom-3">
|
<div className="absolute right-3 bottom-3 flex items-center gap-2">
|
||||||
|
{hasMore && (
|
||||||
|
<Dropdown>
|
||||||
|
<DropdownTrigger>
|
||||||
|
<button
|
||||||
|
className={clsx(
|
||||||
|
"rounded-full p-2 transition-all duration-200",
|
||||||
|
"bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:scale-105 active:scale-95 hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||||
|
)}
|
||||||
|
aria-label="More actions"
|
||||||
|
title="More actions"
|
||||||
|
>
|
||||||
|
<MoreVertical size={18} />
|
||||||
|
</button>
|
||||||
|
</DropdownTrigger>
|
||||||
|
<DropdownMenu
|
||||||
|
aria-label="More actions"
|
||||||
|
onAction={(key) => {
|
||||||
|
if (key === 'import-json' && onImportJson) {
|
||||||
|
onImportJson();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownItem key="import-json" isDisabled={importDisabled || isImporting}>
|
||||||
|
{isImporting ? 'Importing Assistant (JSON)…' : 'Import Assistant (JSON)'}
|
||||||
|
</DropdownItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
disabled={isSubmitting || submitDisabled || !value.trim()}
|
disabled={isSubmitting || submitDisabled || !value.trim()}
|
||||||
|
|
@ -62,6 +102,8 @@ export const TextareaWithSend = forwardRef<HTMLTextAreaElement, TextareaWithSend
|
||||||
: "bg-gray-100 dark:bg-gray-800 text-gray-400 dark:text-gray-500",
|
: "bg-gray-100 dark:bg-gray-800 text-gray-400 dark:text-gray-500",
|
||||||
isSubmitting ? "opacity-50" : "hover:scale-105 active:scale-95"
|
isSubmitting ? "opacity-50" : "hover:scale-105 active:scale-95"
|
||||||
)}
|
)}
|
||||||
|
aria-label="Send"
|
||||||
|
title="Send"
|
||||||
>
|
>
|
||||||
{isSubmitting ? (
|
{isSubmitting ? (
|
||||||
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-current"></div>
|
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-current"></div>
|
||||||
|
|
@ -75,4 +117,4 @@ export const TextareaWithSend = forwardRef<HTMLTextAreaElement, TextareaWithSend
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
TextareaWithSend.displayName = 'TextareaWithSend';
|
TextareaWithSend.displayName = 'TextareaWithSend';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import clsx from 'clsx';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import mascotImage from '@/public/mascot.png';
|
import mascotImage from '@/public/mascot.png';
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Upload, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||||
import { TextareaWithSend } from "@/app/components/ui/textarea-with-send";
|
import { TextareaWithSend } from "@/app/components/ui/textarea-with-send";
|
||||||
import { Workflow } from '../../lib/types/workflow_types';
|
import { Workflow } from '../../lib/types/workflow_types';
|
||||||
import { PictureImg } from '@/components/ui/picture-img';
|
import { PictureImg } from '@/components/ui/picture-img';
|
||||||
|
|
@ -276,6 +276,9 @@ export function BuildAssistantSection() {
|
||||||
setPromptError(null);
|
setPromptError(null);
|
||||||
}}
|
}}
|
||||||
onSubmit={handleCreateAssistant}
|
onSubmit={handleCreateAssistant}
|
||||||
|
onImportJson={handleImportJsonClick}
|
||||||
|
isImporting={importLoading}
|
||||||
|
importDisabled={importLoading}
|
||||||
isSubmitting={isCreating}
|
isSubmitting={isCreating}
|
||||||
placeholder="Example: Build me an assistant to manage my email and calendar..."
|
placeholder="Example: Build me an assistant to manage my email and calendar..."
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|
@ -287,7 +290,7 @@ export function BuildAssistantSection() {
|
||||||
promptError && "border-red-500 focus:ring-red-500/20",
|
promptError && "border-red-500 focus:ring-red-500/20",
|
||||||
!userPrompt && "animate-pulse border-2 border-indigo-500/40 dark:border-indigo-400/40 shadow-lg shadow-indigo-500/20 dark:shadow-indigo-400/20"
|
!userPrompt && "animate-pulse border-2 border-indigo-500/40 dark:border-indigo-400/40 shadow-lg shadow-indigo-500/20 dark:shadow-indigo-400/20"
|
||||||
)}
|
)}
|
||||||
rows={3}
|
rows={4}
|
||||||
autoFocus
|
autoFocus
|
||||||
autoResize
|
autoResize
|
||||||
/>
|
/>
|
||||||
|
|
@ -298,40 +301,7 @@ export function BuildAssistantSection() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Separation line with OR */}
|
{/* Removed separation line and secondary action per request */}
|
||||||
<div className="relative my-3">
|
|
||||||
<div className="absolute inset-0 flex items-center">
|
|
||||||
<div className="w-full border-t border-gray-300 dark:border-gray-600"></div>
|
|
||||||
</div>
|
|
||||||
<div className="relative flex justify-center text-sm">
|
|
||||||
<span className="bg-white dark:bg-gray-800 px-3 text-gray-500 dark:text-gray-400">OR</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Action buttons */}
|
|
||||||
<div className="flex gap-3 justify-start">
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
size="sm"
|
|
||||||
onClick={handleImportJsonClick}
|
|
||||||
type="button"
|
|
||||||
startContent={<Upload size={14} />}
|
|
||||||
className="bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-600 border border-gray-300 dark:border-gray-600"
|
|
||||||
disabled={importLoading}
|
|
||||||
>
|
|
||||||
{importLoading ? 'Importing...' : 'Import JSON'}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
size="sm"
|
|
||||||
onClick={handleCreateAssistant}
|
|
||||||
isLoading={isCreating}
|
|
||||||
type="button"
|
|
||||||
className="bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-600 border border-gray-300 dark:border-gray-600"
|
|
||||||
>
|
|
||||||
Go to Builder
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{importError && (
|
{importError && (
|
||||||
<p className="text-sm text-red-500 mt-2">
|
<p className="text-sm text-red-500 mt-2">
|
||||||
|
|
@ -531,4 +501,4 @@ export function BuildAssistantSection() {
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue