Skip to content

Commit beac049

Browse files
committed
feat: overhaul UX and naming
This commit overhauls the UX of the edit page, which includes: - Puts configuration options into a accordion - Adds "dynamic instructions" which are a special context tool. - Adds a tool catalog for importing tools - Adds a "create a tool" modal for creating tools - Add support for running custom tools - Adds a code editor for editing scripts through Monaco - Adjusts a lot of styling to make the page more readable and usable Signed-off-by: tylerslaton <[email protected]>
1 parent 35cdab9 commit beac049

File tree

22 files changed

+857
-616
lines changed

22 files changed

+857
-616
lines changed

app/edit/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ function EditFile() {
3232
<div className="absolute left-6 top-6">
3333
<ScriptNav collapsed={collapsed} setCollapsed={setCollapsed}/>
3434
</div>
35-
<div className="h-full overflow-auto w-full border-r-2 dark:border-zinc-800 p-6">
36-
<Configure file={file} />
35+
<div className={`h-full overflow-auto w-full border-r-2 dark:border-zinc-800 p-6 ${collapsed ? '' : 'xl:px-20' }`}>
36+
<Configure collapsed={collapsed}/>
3737
</div>
3838
<Script messagesHeight='h-[93%]' className={`p-6 overflow-auto ${collapsed ? 'col-span-3 px-32' : '' }`} />
3939
</div>

app/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ export default function Home() {
66
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
77
<div className="inline-block max-w-lg text-center justify-center">
88
<h1 className={title() + ' drop-shadow-xl'}>
9-
My Scripts
9+
My Assistants
1010
</h1>
1111
<h2 className={subtitle({class: "mt-4 mb-10"})}>
12-
Select a script below to run.
12+
Select an assistant below to interact with or edit.
1313
</h2>
1414
</div>
1515

components/edit/configure.tsx

Lines changed: 78 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,39 @@
1-
import {useState, useEffect, useCallback, useContext} from "react";
2-
import {Tool} from "@gptscript-ai/gptscript";
3-
import Imports from "@/components/edit/configure/imports";
1+
import {useCallback, useContext, useEffect} from "react";
2+
import RemoteImports from "@/components/edit/configure/imports";
43
import Loading from "@/components/loading";
5-
import CustomTool from "@/components/edit/configure/customTool";
64
import Models from "@/components/edit/configure/models";
75
import Visibility from "@/components/edit/configure/visibility";
6+
import Code from "@/components/edit/configure/code";
87
import {EditContext} from "@/contexts/edit";
9-
import { ScriptContext } from "@/contexts/script";
8+
import { GoLightBulb, GoPlus, GoTools } from "react-icons/go";
9+
import { HiCog, HiOutlinePlus } from "react-icons/hi2";
10+
import { LuCircuitBoard } from "react-icons/lu";
1011
import {
1112
Textarea,
1213
Input,
1314
Avatar,
1415
Tooltip,
15-
Button,
16-
Select,
17-
SelectItem,
18-
Card,
19-
CardBody,
16+
Accordion,
17+
AccordionItem,
2018
} from "@nextui-org/react";
21-
import {getModels} from "@/actions/models";
22-
import { GoCode, GoPencil, GoPlay } from "react-icons/go";
23-
import Code from "@/components/edit/configure/code";
19+
import { PiToolboxBold } from "react-icons/pi";
2420

2521
interface ConfigureProps {
26-
file: string;
27-
tool?: Tool;
22+
collapsed?: boolean;
2823
className?: string;
29-
custom?: boolean;
3024
}
3125

32-
const Configure: React.FC<ConfigureProps> = ({file, className, custom}) => {
26+
const Configure: React.FC<ConfigureProps> = ({className, collapsed}) => {
3327
const {
3428
root,
3529
setRoot,
36-
tools,
37-
setTools,
30+
models,
3831
loading,
39-
newestToolName,
4032
visibility,
4133
setVisibility,
34+
dynamicInstructions,
35+
setDynamicInstructions,
4236
} = useContext(EditContext);
43-
const { setSubTool } = useContext(ScriptContext);
44-
const [models, setModels] = useState<string[]>([]);
45-
const [instructionsType, setInstructionsType] = useState<string>("prompt");
46-
47-
useEffect(() => {
48-
getModels().then((m) => {
49-
setModels(m)
50-
})
51-
}, []);
5237

5338
const abbreviate = (name: string) => {
5439
const words = name.split(/(?=[A-Z])|[\s_-]/);
@@ -60,117 +45,94 @@ const Configure: React.FC<ConfigureProps> = ({file, className, custom}) => {
6045
setRoot({...root, tools: newTools});
6146
}, [root]);
6247

63-
const setRootContexts = useCallback((newContexts: string[]) => {
64-
setRoot({...root, context: newContexts});
65-
}, [root]);
66-
67-
const setRootAgents = useCallback((newAgents: string[]) => {
68-
setRoot({...root, agents: newAgents});
69-
}, [root]);
70-
71-
if (loading) return <Loading>Loading your script's details...</Loading>;
48+
if (loading) return <Loading>Loading your assistant's details...</Loading>;
7249

7350
return (
7451
<>
75-
<div className="w-full">
52+
<div className="flex flex-col w-full justify-center items-center space-y-2 mb-6 mt-10">
7653
<Tooltip
77-
content={`${root.name || "Main"}`}
78-
placement="bottom"
54+
content={"Upload a profile picture for your assistant."}
55+
placement="top"
7956
closeDelay={0.5}
8057
>
8158
<Avatar
82-
size="md"
83-
name={abbreviate(root.name || 'Main')}
84-
className="mx-auto mb-6 mt-4"
59+
size="lg"
60+
icon={<HiOutlinePlus className="text-xl" />}
61+
className="block cursor-pointer mb-6"
8562
classNames={{base: "bg-white p-6 text-sm border dark:border-none dark:bg-zinc-900"}}
8663
/>
8764
</Tooltip>
8865
</div>
8966
<div className="px-2 flex flex-col space-y-4 mb-6">
90-
<Visibility visibility={visibility} setVisibility={setVisibility} />
91-
<Input
92-
color="primary"
93-
variant="bordered"
94-
label="Name"
95-
placeholder="Give your chat bot a name..."
96-
defaultValue={root.name}
97-
onChange={(e) => setRoot({...root, name: e.target.value})}
98-
/>
67+
<div className="relative">
68+
<div className="absolute top-3 right-0 z-40">
69+
<Visibility visibility={visibility} setVisibility={setVisibility} />
70+
</div>
71+
<Input
72+
color="primary"
73+
variant="bordered"
74+
label="Name"
75+
placeholder="Give your assistant a name..."
76+
defaultValue={root.name}
77+
onChange={(e) => setRoot({...root, name: e.target.value})}
78+
/>
79+
</div>
9980
<Textarea
10081
color="primary"
10182
fullWidth
10283
variant="bordered"
10384
label="Description"
104-
placeholder="Describe your script..."
85+
placeholder="Describe what your assistant does..."
10586
defaultValue={root.description}
10687
onChange={(e) => setRoot({...root, description: e.target.value})}
10788
/>
108-
<Select
89+
<Textarea
10990
color="primary"
110-
label="Instructions Type"
91+
fullWidth
11192
variant="bordered"
112-
defaultSelectedKeys={["prompt"]}
113-
onChange={(e) => setInstructionsType(e.target.value)}
114-
>
115-
<SelectItem key="prompt" value="prompt" textValue="Prompt" startContent={<GoPencil />}>
116-
<h1>Prompt</h1>
117-
<p className="text-default-500 text-tiny">Standard - Describe behavior using natural language.</p>
118-
</SelectItem>
119-
<SelectItem key="code" value="code" startContent={<GoCode />} textValue="Code">
120-
<h1>Code</h1>
121-
<p className="text-default-500 text-tiny">Advanced - Describe behavior using proramming languages.</p>
122-
</SelectItem>
123-
</Select>
124-
{(instructionsType !== "code") &&
125-
<>
126-
<Textarea
127-
color="primary"
128-
fullWidth
129-
variant="bordered"
130-
label="Instructions"
131-
placeholder="Describe your how your script should behave..."
132-
defaultValue={root.instructions}
133-
onChange={(e) => setRoot({...root, instructions: e.target.value})}
134-
/>
135-
<Models options={models} defaultValue={root.modelName} onChange={(model) => setRoot({...root, modelName: model})} />
136-
<Imports className="py-2"
93+
label="Instructions"
94+
placeholder="Give your assistant instructions on how to behave..."
95+
defaultValue={root.instructions}
96+
onChange={(e) => setRoot({...root, instructions: e.target.value})}
97+
/>
98+
<Accordion isCompact fullWidth selectionMode="multiple">
99+
<AccordionItem
100+
aria-label="dynamic-instructions"
101+
title={<h1>Dynamic Instructions</h1>}
102+
startContent={<LuCircuitBoard />}
103+
classNames={{content: "p-10 pt-6"}}
104+
>
105+
<div className="flex bg-primary-50 rounded-xl p-4 mb-4 text-tiny italic text-primary-500 items-center space-x-4">
106+
<GoLightBulb className={`inline mb-1 text-sm ${collapsed ? 'w-[200px] ': 'w-fit'} `}/>
107+
<p>
108+
Augment your instructions with code that can pull information from local or remote systems.
109+
</p>
110+
</div>
111+
<Code label="Code" code={dynamicInstructions} onChange={setDynamicInstructions} />
112+
{/* <div className="my-4"/>
113+
<Code label="Dependencies" code={'// package.json'} onChange={(code) => {}} /> */}
114+
</AccordionItem>
115+
<AccordionItem
116+
aria-label="remote-tools"
117+
title={<h1>Tools</h1>}
118+
startContent={<PiToolboxBold />}
119+
classNames={{content: "p-10 pt-6"}}
120+
>
121+
<RemoteImports
137122
tools={root.tools}
138-
contexts={root.context}
139-
agents={root.agents}
140-
setAgents={setRootAgents}
141-
setContexts={setRootContexts}
142-
setTools={setRootTools} label={"Tool"}
123+
setTools={setRootTools}
124+
collapsed={collapsed}
143125
/>
144-
{!custom && tools && tools.length > 0 &&
145-
<Card className="w-full pt-2 pb-6" shadow="sm">
146-
<CardBody>
147-
<h1 className="mb-4 px-2 text-lg">Local Tools</h1>
148-
<div className="flex flex-col space-y-2 px-2">
149-
{tools && tools.map((customTool, i) => (
150-
<div className="flex space-x-2 ">
151-
<CustomTool tool={customTool} file={file} models={models}/>
152-
<Button
153-
startContent={<GoPlay />}
154-
color="primary" variant="flat"
155-
size="sm"
156-
className="text-sm"
157-
isIconOnly
158-
onPress={() => setSubTool(customTool.name || '')}
159-
/>
160-
</div>
161-
))}
162-
</div>
163-
</CardBody>
164-
</Card>
165-
}
166-
</>
167-
}
168-
{(instructionsType === "code") &&
169-
<Code
170-
code={root.instructions || ''}
171-
onChange={(code) => setRoot({...root, instructions: code || ''})}
172-
/>
173-
}
126+
</AccordionItem>
127+
<AccordionItem
128+
aria-label="advanced"
129+
title={<h1>Advanced</h1>}
130+
startContent={<HiCog />}
131+
classNames={{content: "p-10 pt-6 h-[500px]"}}
132+
>
133+
<Models options={models} defaultValue={root.modelName} onChange={(model) => setRoot({...root, modelName: model})} />
134+
</AccordionItem>
135+
</Accordion>
174136
</div>
175137
</>
176138
);

components/edit/configure/code.tsx

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,44 @@ import Editor from "@monaco-editor/react";
22
import {useEffect, useState} from "react";
33
import {Button} from "@nextui-org/button";
44
import {BsArrowsFullscreen} from "react-icons/bs";
5-
import { Modal, ModalBody, ModalContent, ModalHeader } from "@nextui-org/react";
5+
import { Divider, Modal, ModalBody, ModalContent, ModalHeader } from "@nextui-org/react";
66
import {useTheme} from "next-themes";
77

88
export enum Language {
99
Javascript = "javascript",
1010
Python = "python",
11-
Bash = "bash",
11+
Bash = "shell",
1212
}
1313

1414
interface CodeProps {
1515
code: string;
1616
onChange: (value: string) => void;
17+
label?: string;
1718
}
1819

19-
const Code = ({code, onChange}: CodeProps) => {
20+
const Code = ({code, onChange, label = "Code"}: CodeProps) => {
2021
const [isFullscreen, setIsFullscreen] = useState(false);
2122
const [language, setLanguage] = useState<Language>(Language.Javascript);
2223
const {theme, setTheme} = useTheme();
2324

24-
useEffect(() => {
25-
console.log(language)
26-
}, [language])
27-
2825
return (
2926
<div className="border-2 dark:border-zinc-700 rounded-xl">
3027
<div className="flex justify-between items-center pr-2">
31-
<select className="text-primary pl-2 dark:bg-black text-sm capitalize"
32-
onChange={((e) => {setLanguage(e.target.value as Language)})}
33-
>
34-
<option>{Language.Javascript}</option>
35-
<option>{Language.Python}</option>
36-
<option>{Language.Bash}</option>
37-
</select>
28+
<div className="flex">
29+
{label &&
30+
<>
31+
<p className="text-primary-500 font-medium pl-3 text-tiny">{label}</p>
32+
<Divider orientation="vertical" className="h-4 ml-2"/>
33+
</>
34+
}
35+
<select className="pl-2 dark:bg-black text-tiny capitalize"
36+
onChange={((e) => {setLanguage(e.target.value as Language)})}
37+
>
38+
<option>{Language.Javascript}</option>
39+
<option>{Language.Python}</option>
40+
<option>{Language.Bash}</option>
41+
</select>
42+
</div>
3843
<Button
3944
startContent={<BsArrowsFullscreen/>}
4045
isIconOnly size="sm"
@@ -43,6 +48,22 @@ const Code = ({code, onChange}: CodeProps) => {
4348
onPress={() => setIsFullscreen(true)}
4449
/>
4550
</div>
51+
<div className="pb-1 border-t-1 dark:border-t-zinc-700">
52+
<Editor
53+
height="35vh"
54+
language={language}
55+
theme={theme === "dark" ? "hc-black" : "vs-light"}
56+
value={code}
57+
onChange={(code) => {onChange(code || '')}}
58+
options={{
59+
inlineSuggest: { enabled: true },
60+
formatOnType: true,
61+
padding: { top: 10},
62+
minimap: { enabled: false },
63+
}}
64+
/>
65+
</div>
66+
4667
<Modal
4768
isOpen={isFullscreen}
4869
onOpenChange={setIsFullscreen}
@@ -79,21 +100,6 @@ const Code = ({code, onChange}: CodeProps) => {
79100
</ModalBody>
80101
</ModalContent>
81102
</Modal>
82-
<div className="pb-1 border-t-1 dark:border-t-zinc-700">
83-
<Editor
84-
height="35vh"
85-
language={language}
86-
theme={theme === "dark" ? "hc-black" : "vs-light"}
87-
value={code}
88-
onChange={(code) => {onChange(code || '')}}
89-
options={{
90-
inlineSuggest: { enabled: true },
91-
formatOnType: true,
92-
padding: { top: 10},
93-
minimap: { enabled: false },
94-
}}
95-
/>
96-
</div>
97103
</div>
98104
);
99105
}

0 commit comments

Comments
 (0)