From e6fdd2d5c5824a48c317da7999105cca2c304181 Mon Sep 17 00:00:00 2001 From: Kyle Corbitt Date: Mon, 26 Jun 2023 06:26:19 -0700 Subject: [PATCH] prettifying the ux --- package.json | 2 - pnpm-lock.yaml | 14 --- .../OutputsTable/EditableVariantLabel.tsx | 40 -------- .../OutputsTable/NewVariantButton.tsx | 14 ++- src/components/OutputsTable/OutputCell.tsx | 21 ++-- ...{ScenarioHeader.tsx => ScenarioEditor.tsx} | 5 +- src/components/OutputsTable/VariantHeader.tsx | 99 +++++++++++-------- src/components/OutputsTable/index.tsx | 47 ++++----- src/components/constants.ts | 6 ++ src/components/nav/AppNav.tsx | 10 +- src/pages/index.tsx | 79 --------------- .../api/routers/promptVariants.router.ts | 17 ++++ src/utils/theme.ts | 9 +- 13 files changed, 141 insertions(+), 222 deletions(-) delete mode 100644 src/components/OutputsTable/EditableVariantLabel.tsx rename src/components/OutputsTable/{ScenarioHeader.tsx => ScenarioEditor.tsx} (94%) create mode 100644 src/components/constants.ts delete mode 100644 src/pages/index.tsx diff --git a/package.json b/package.json index faf785f..41e89b9 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,6 @@ "@emotion/react": "^11.11.1", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.11.0", - "@fontsource/poppins": "^5.0.3", - "@fontsource/roboto": "^5.0.3", "@monaco-editor/react": "^4.5.1", "@next-auth/prisma-adapter": "^1.0.5", "@prisma/client": "^4.14.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1eee158..809385a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,12 +20,6 @@ dependencies: '@emotion/styled': specifier: ^11.11.0 version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.6)(react@18.2.0) - '@fontsource/poppins': - specifier: ^5.0.3 - version: 5.0.3 - '@fontsource/roboto': - specifier: ^5.0.3 - version: 5.0.3 '@monaco-editor/react': specifier: ^4.5.1 version: 4.5.1(monaco-editor@0.39.0)(react-dom@18.2.0)(react@18.2.0) @@ -1682,14 +1676,6 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@fontsource/poppins@5.0.3: - resolution: {integrity: sha512-5lL2vmvmh4zbknTh1nVH9pTWyhYqAlYMHIAnkNhqo5qwMZGlr+coM1dtMwiQHRBgmHAl3ZvJ35Bj0s8cpmXZbg==} - dev: false - - /@fontsource/roboto@5.0.3: - resolution: {integrity: sha512-jbZDFwEFARDlo8TqG7th/xjhuq87GYfFpFb+uxuy+0Ng1bhRVgBRWlLj8+WIKhCTOr+h4QXbjpybLWFLUirOwQ==} - dev: false - /@humanwhocodes/config-array@0.11.10: resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} engines: {node: '>=10.10.0'} diff --git a/src/components/OutputsTable/EditableVariantLabel.tsx b/src/components/OutputsTable/EditableVariantLabel.tsx deleted file mode 100644 index 6153977..0000000 --- a/src/components/OutputsTable/EditableVariantLabel.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useRef } from "react"; -import { type PromptVariant } from "./types"; -import { api } from "~/utils/api"; -import { useHandledAsyncCallback } from "~/utils/hooks"; -import { Heading } from "@chakra-ui/react"; - -export default function EditableVariantLabel(props: { variant: PromptVariant }) { - const labelRef = useRef(null); - - const mutation = api.promptVariants.update.useMutation(); - - const [onBlur] = useHandledAsyncCallback(async () => { - const newLabel = labelRef.current?.innerText; - if (newLabel && newLabel !== props.variant.label) { - await mutation.mutateAsync({ - id: props.variant.id, - updates: { label: newLabel }, - }); - } - }, [mutation, props.variant.id, props.variant.label]); - - return ( - - {props.variant.label} - - ); -} diff --git a/src/components/OutputsTable/NewVariantButton.tsx b/src/components/OutputsTable/NewVariantButton.tsx index c853b1f..8089c86 100644 --- a/src/components/OutputsTable/NewVariantButton.tsx +++ b/src/components/OutputsTable/NewVariantButton.tsx @@ -2,6 +2,7 @@ import { Button, Tooltip } from "@chakra-ui/react"; import { BsPlus } from "react-icons/bs"; import { api } from "~/utils/api"; import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks"; +import { cellPadding, headerMinHeight } from "../constants"; export default function NewVariantButton() { const experiment = useExperiment(); @@ -21,16 +22,19 @@ export default function NewVariantButton() { ); diff --git a/src/components/OutputsTable/OutputCell.tsx b/src/components/OutputsTable/OutputCell.tsx index 0d54f8e..391efa3 100644 --- a/src/components/OutputsTable/OutputCell.tsx +++ b/src/components/OutputsTable/OutputCell.tsx @@ -3,6 +3,13 @@ import { PromptVariant, Scenario } from "./types"; import { Center, Spinner, Text } from "@chakra-ui/react"; import { useExperiment } from "~/utils/hooks"; import { JSONSerializable } from "~/server/types"; +import { cellPadding } from "../constants"; + +const CellShell = ({ children }: { children: React.ReactNode }) => ( +
+ {children} +
+); export default function OutputCell({ scenario, @@ -36,23 +43,21 @@ export default function OutputCell({ if (disabledReason) return ( -
+ {disabledReason} -
+ ); if (output.isLoading) return ( -
+ -
+ ); - if (!output.data) return
No output
; + if (!output.data) return No output; return ( -
- {JSON.stringify(output.data.output.choices[0].message.content, null, 2)} -
+ {JSON.stringify(output.data.output.choices[0].message.content, null, 2)} ); } diff --git a/src/components/OutputsTable/ScenarioHeader.tsx b/src/components/OutputsTable/ScenarioEditor.tsx similarity index 94% rename from src/components/OutputsTable/ScenarioHeader.tsx rename to src/components/OutputsTable/ScenarioEditor.tsx index 94dd51b..6e0342b 100644 --- a/src/components/OutputsTable/ScenarioHeader.tsx +++ b/src/components/OutputsTable/ScenarioEditor.tsx @@ -6,8 +6,9 @@ import { useState } from "react"; import ResizeTextarea from "react-textarea-autosize"; import { Box, Button, Flex, HStack, Stack, Textarea } from "@chakra-ui/react"; +import { cellPadding } from "../constants"; -export default function ScenarioHeader({ scenario }: { scenario: Scenario }) { +export default function ScenarioEditor({ scenario }: { scenario: Scenario }) { const savedValues = scenario.variableValues as Record; const utils = api.useContext(); @@ -30,7 +31,7 @@ export default function ScenarioHeader({ scenario }: { scenario: Scenario }) { }, [mutation, values]); return ( - + {variableLabels.map((key) => { const value = values[key] ?? ""; const layoutDirection = value.length > 20 ? "column" : "row"; diff --git a/src/components/OutputsTable/VariantHeader.tsx b/src/components/OutputsTable/VariantHeader.tsx index 79172c5..6d981b0 100644 --- a/src/components/OutputsTable/VariantHeader.tsx +++ b/src/components/OutputsTable/VariantHeader.tsx @@ -1,53 +1,66 @@ -import { useCallback } from "react"; -import type { PromptVariant } from "./types"; +import { useRef } from "react"; +import { type PromptVariant } from "./types"; import { api } from "~/utils/api"; -import { type JSONSerializable } from "~/server/types"; -import VariantConfigEditor from "./VariantConfigEditor"; -import EditableVariantLabel from "./EditableVariantLabel"; -import { Stack, useToast } from "@chakra-ui/react"; +import { useHandledAsyncCallback } from "~/utils/hooks"; +import { Button, HStack, Heading, Tooltip } from "@chakra-ui/react"; +import { BsX } from "react-icons/bs"; +import { cellPadding, headerMinHeight } from "../constants"; -export default function VariantHeader({ variant }: { variant: PromptVariant }) { - const replaceWithConfig = api.promptVariants.replaceWithConfig.useMutation(); +export default function VariantHeader(props: { variant: PromptVariant }) { const utils = api.useContext(); - const toast = useToast(); - const onSave = useCallback( - async (currentConfig: string) => { - let parsedConfig: JSONSerializable; - try { - parsedConfig = JSON.parse(currentConfig) as JSONSerializable; - } catch (e) { - toast({ - title: "Invalid JSON", - description: "Please fix the JSON before saving.", - status: "error", - }); - return; - } - - if (parsedConfig === null) { - toast({ - title: "Invalid JSON", - description: "Please fix the JSON before saving.", - status: "error", - }); - return; - } - - await replaceWithConfig.mutateAsync({ - id: variant.id, - config: currentConfig, + const labelRef = useRef(null); + const updateMutation = api.promptVariants.update.useMutation(); + const [onSaveLabel] = useHandledAsyncCallback(async () => { + const newLabel = labelRef.current?.innerText; + if (newLabel && newLabel !== props.variant.label) { + await updateMutation.mutateAsync({ + id: props.variant.id, + updates: { label: newLabel }, }); + } + }, [updateMutation, props.variant.id, props.variant.label]); - await utils.promptVariants.list.invalidate(); - }, - [variant.id, replaceWithConfig, utils.promptVariants.list, toast] - ); + const hideMutation = api.promptVariants.hide.useMutation(); + const [onHide] = useHandledAsyncCallback(async () => { + await hideMutation.mutateAsync({ + id: props.variant.id, + }); + await utils.promptVariants.list.invalidate(); + }, [hideMutation, props.variant.id]); return ( - - - - + + + {props.variant.label} + + + + + ); } diff --git a/src/components/OutputsTable/index.tsx b/src/components/OutputsTable/index.tsx index 31a08a6..33724b3 100644 --- a/src/components/OutputsTable/index.tsx +++ b/src/components/OutputsTable/index.tsx @@ -1,15 +1,21 @@ import { RouterOutputs, api } from "~/utils/api"; import { Scenario, type PromptVariant } from "./types"; -import VariantHeader from "./VariantHeader"; import OutputCell from "./OutputCell"; -import ScenarioHeader from "./ScenarioHeader"; +import ScenarioEditor from "./ScenarioEditor"; import React, { useState } from "react"; -import { Box, Grid, GridItem, Heading } from "@chakra-ui/react"; +import { Box, Grid, GridItem, Heading, SystemStyleObject } from "@chakra-ui/react"; import NewScenarioButton from "./NewScenarioButton"; import NewVariantButton from "./NewVariantButton"; -import EditableVariantLabel from "./EditableVariantLabel"; +import VariantHeader from "./VariantHeader"; import VariantConfigEditor from "./VariantConfigEditor"; +const stickyHeaderStyle: SystemStyleObject = { + position: "sticky", + top: 0, + backgroundColor: "#fff", + zIndex: 1, +}; + const ScenarioRow = (props: { scenario: Scenario; variants: PromptVariant[] }) => { const [isHovered, setIsHovered] = useState(false); @@ -22,16 +28,16 @@ const ScenarioRow = (props: { scenario: Scenario; variants: PromptVariant[] }) = setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} - sx={isHovered ? highlightStyle : null} + sx={isHovered ? highlightStyle : undefined} > - + {props.variants.map((variant) => ( setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} - sx={isHovered ? highlightStyle : null} + sx={isHovered ? highlightStyle : undefined} > @@ -57,14 +63,12 @@ export default function OutputsTable({ experimentId }: { experimentId: string | *": { borderColor: "gray.300", borderBottomWidth: 1, borderRightWidth: 1, - paddingX: 4, - paddingY: 2, }, "> *:last-child": { borderRightWidth: 0, @@ -72,25 +76,24 @@ export default function OutputsTable({ experimentId }: { experimentId: string | }} > - - Scenario - + + + Scenario + + {variants.data.map((variant) => ( - - + + ))} *" selector on Grid + style={{ borderRightWidth: 0, borderBottomWidth: 0 }} + sx={stickyHeaderStyle} + className="new-variant-button" > diff --git a/src/components/constants.ts b/src/components/constants.ts new file mode 100644 index 0000000..e48d297 --- /dev/null +++ b/src/components/constants.ts @@ -0,0 +1,6 @@ +export const cellPadding = { + x: 4, + y: 2, +}; + +export const headerMinHeight = 8; diff --git a/src/components/nav/AppNav.tsx b/src/components/nav/AppNav.tsx index 97d73cd..5a8f5a1 100644 --- a/src/components/nav/AppNav.tsx +++ b/src/components/nav/AppNav.tsx @@ -3,13 +3,17 @@ import Head from "next/head"; export default function AppNav(props: { children: React.ReactNode; title?: string }) { return ( - + {props.title ? `${props.title} | Prompt Bench` : "Prompt Bench"} {/* Placeholder for now */} - - {props.children} + + Nav Sidebar + + + {props.children} + ); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx deleted file mode 100644 index af531c1..0000000 --- a/src/pages/index.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import styles from "./index.module.css"; -import { signIn, signOut, useSession } from "next-auth/react"; -import Head from "next/head"; -import Link from "next/link"; -import { api } from "~/utils/api"; - -export default function Home() { - const hello = api.example.hello.useQuery({ text: "from tRPC" }); - - return ( - <> - - Create T3 App - - - -
-
-

- Create T3 App -

-
- -

First Steps →

-
- Just the basics - Everything you need to know to set up your database and - authentication. -
- - -

Documentation →

-
- Learn more about Create T3 App, the libraries it uses, and how to deploy it. -
- -
-
-

- {hello.data ? hello.data.greeting : "Loading tRPC query..."} -

- -
-
-
- - ); -} - -function AuthShowcase() { - const { data: sessionData } = useSession(); - - const { data: secretMessage } = api.example.getSecretMessage.useQuery( - undefined, // no input - { enabled: sessionData?.user !== undefined } - ); - - return ( -
-

- {sessionData && Logged in as {sessionData.user?.name}} - {secretMessage && - {secretMessage}} -

- -
- ); -} diff --git a/src/server/api/routers/promptVariants.router.ts b/src/server/api/routers/promptVariants.router.ts index c673fd6..b1f981a 100644 --- a/src/server/api/routers/promptVariants.router.ts +++ b/src/server/api/routers/promptVariants.router.ts @@ -75,6 +75,23 @@ export const promptVariantsRouter = createTRPCRouter({ }); }), + hide: publicProcedure + .input( + z.object({ + id: z.string(), + }) + ) + .mutation(async ({ input }) => { + return await prisma.promptVariant.update({ + where: { + id: input.id, + }, + data: { + visible: false, + }, + }); + }), + replaceWithConfig: publicProcedure .input( z.object({ diff --git a/src/utils/theme.ts b/src/utils/theme.ts index cecce29..41671d0 100644 --- a/src/utils/theme.ts +++ b/src/utils/theme.ts @@ -1,11 +1,12 @@ import { extendTheme } from "@chakra-ui/react"; -import "@fontsource/poppins"; -import "@fontsource/roboto"; + +const systemFont = + 'ui-sans-serif, -apple-system, "system-ui", "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"'; const theme = extendTheme({ fonts: { - heading: "Poppins, sans-serif", - body: "Roboto, sans-serif", + heading: systemFont, + body: systemFont, }, });