From 85344772366922c794ebdb81a842ceff0ad42aec Mon Sep 17 00:00:00 2001 From: Kyle Corbitt Date: Fri, 23 Jun 2023 20:00:46 -0700 Subject: [PATCH] can add scenarios and it mostly works --- package.json | 4 + pnpm-lock.yaml | 77 +++++++++++++++++ .../OutputsTable/EditableVariantLabel.tsx | 5 ++ .../OutputsTable/NewScenarioButton.tsx | 34 ++++++++ src/components/OutputsTable/OutputCell.tsx | 33 ++++++-- .../OutputsTable/ScenarioHeader.tsx | 49 +++++++---- .../OutputsTable/VariantConfigEditor.tsx | 7 +- src/components/OutputsTable/VariantHeader.tsx | 10 +-- src/components/OutputsTable/index.tsx | 82 ++++++++++--------- src/pages/_app.tsx | 3 +- src/pages/experiments/[id].tsx | 2 +- src/server/api/routers/scenarios.router.ts | 28 +++++++ src/utils/theme.ts | 12 +++ 13 files changed, 278 insertions(+), 68 deletions(-) create mode 100644 src/components/OutputsTable/NewScenarioButton.tsx create mode 100644 src/utils/theme.ts diff --git a/package.json b/package.json index f8dd22f..faf785f 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ "@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", @@ -34,6 +36,8 @@ "next-auth": "^4.22.1", "react": "18.2.0", "react-dom": "18.2.0", + "react-icons": "^4.10.1", + "react-textarea-autosize": "^8.5.0", "superjson": "1.12.2", "tsx": "^3.12.7", "zod": "^3.21.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 03d7926..1eee158 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,12 @@ 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) @@ -74,6 +80,12 @@ dependencies: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-icons: + specifier: ^4.10.1 + version: 4.10.1(react@18.2.0) + react-textarea-autosize: + specifier: ^8.5.0 + version: 8.5.0(@types/react@18.2.6)(react@18.2.0) superjson: specifier: 1.12.2 version: 1.12.2 @@ -1670,6 +1682,14 @@ 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'} @@ -4135,6 +4155,14 @@ packages: use-sidecar: 1.1.2(@types/react@18.2.6)(react@18.2.0) dev: false + /react-icons@4.10.1(react@18.2.0): + resolution: {integrity: sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==} + peerDependencies: + react: '*' + dependencies: + react: 18.2.0 + dev: false + /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -4198,6 +4226,20 @@ packages: tslib: 2.5.3 dev: false + /react-textarea-autosize@8.5.0(@types/react@18.2.6)(react@18.2.0): + resolution: {integrity: sha512-cp488su3U9RygmHmGpJp0KEt0i/+57KCK33XVPH+50swVRBhIZYh0fGduz2YLKXwl9vSKBZ9HUXcg9PQXUXqIw==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.22.5 + react: 18.2.0 + use-composed-ref: 1.3.0(react@18.2.0) + use-latest: 1.2.1(@types/react@18.2.6)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -4658,6 +4700,41 @@ packages: tslib: 2.5.3 dev: false + /use-composed-ref@1.3.0(react@18.2.0): + resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /use-isomorphic-layout-effect@1.1.2(@types/react@18.2.6)(react@18.2.0): + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.6 + react: 18.2.0 + dev: false + + /use-latest@1.2.1(@types/react@18.2.6)(react@18.2.0): + resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.6 + react: 18.2.0 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.6)(react@18.2.0) + dev: false + /use-sidecar@1.1.2(@types/react@18.2.6)(react@18.2.0): resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} diff --git a/src/components/OutputsTable/EditableVariantLabel.tsx b/src/components/OutputsTable/EditableVariantLabel.tsx index 5188678..b4db3d3 100644 --- a/src/components/OutputsTable/EditableVariantLabel.tsx +++ b/src/components/OutputsTable/EditableVariantLabel.tsx @@ -26,7 +26,12 @@ export default function EditableVariantLabel(props: { variant: PromptVariant }) ref={labelRef} contentEditable suppressContentEditableWarning + borderWidth={1} + borderColor="transparent" + _hover={{ borderColor: "gray.300" }} + _focus={{ borderColor: "blue.500", outline: "none" }} onBlur={onBlur} + py={2} > {props.variant.label} diff --git a/src/components/OutputsTable/NewScenarioButton.tsx b/src/components/OutputsTable/NewScenarioButton.tsx new file mode 100644 index 0000000..68801cb --- /dev/null +++ b/src/components/OutputsTable/NewScenarioButton.tsx @@ -0,0 +1,34 @@ +import { Button } from "@chakra-ui/react"; +import { BsPlus } from "react-icons/bs"; +import { api } from "~/utils/api"; +import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks"; + +export default function NewScenarioButton() { + const experiment = useExperiment(); + const mutation = api.scenarios.create.useMutation(); + const utils = api.useContext(); + + const [onClick] = useHandledAsyncCallback(async () => { + await mutation.mutateAsync({ + experimentId: experiment.data!.id, + }); + await utils.scenarios.list.invalidate(); + }, [mutation]); + + return ( + + ); +} diff --git a/src/components/OutputsTable/OutputCell.tsx b/src/components/OutputsTable/OutputCell.tsx index 3e1858e..a112363 100644 --- a/src/components/OutputsTable/OutputCell.tsx +++ b/src/components/OutputsTable/OutputCell.tsx @@ -1,6 +1,7 @@ import { api } from "~/utils/api"; import { PromptVariant, Scenario } from "./types"; -import { Center } from "@chakra-ui/react"; +import { Center, Text } from "@chakra-ui/react"; +import { useExperiment } from "~/utils/hooks"; export default function OutputCell({ scenario, @@ -9,12 +10,32 @@ export default function OutputCell({ scenario: Scenario; variant: PromptVariant; }) { - const output = api.outputs.get.useQuery({ - scenarioId: scenario.id, - variantId: variant.id, - }); + const experiment = useExperiment(); - if (!output.data) return null; + const experimentVariables = experiment.data?.TemplateVariable.map((v) => v.label) ?? []; + const scenarioVariables = scenario.variableValues as Record; + const templateHasVariables = + experimentVariables.length === 0 || + experimentVariables.some((v) => scenarioVariables[v] !== undefined); + + const output = api.outputs.get.useQuery( + { + scenarioId: scenario.id, + variantId: variant.id, + }, + { enabled: templateHasVariables } + ); + + if (!templateHasVariables) + return ( +
+ Add a scenario variable to see output +
+ ); + + if (output.isLoading) return
Loading...
; + + if (!output.data) return
No output
; return (
diff --git a/src/components/OutputsTable/ScenarioHeader.tsx b/src/components/OutputsTable/ScenarioHeader.tsx index efa4053..e2cca13 100644 --- a/src/components/OutputsTable/ScenarioHeader.tsx +++ b/src/components/OutputsTable/ScenarioHeader.tsx @@ -1,9 +1,11 @@ import { api } from "~/utils/api"; import { isEqual } from "lodash"; -import { PromptVariant, Scenario } from "./types"; +import { type Scenario } from "./types"; import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks"; import { useState } from "react"; -import { Badge, Button, Flex, HStack, Stack, Textarea } from "@chakra-ui/react"; +import ResizeTextarea from "react-textarea-autosize"; + +import { Box, Button, Flex, HStack, Stack, Textarea } from "@chakra-ui/react"; export default function ScenarioHeader({ scenario }: { scenario: Scenario }) { const savedValues = scenario.variableValues as Record; @@ -30,34 +32,53 @@ export default function ScenarioHeader({ scenario }: { scenario: Scenario }) { return ( {variableLabels.map((key) => { + const value = values[key] ?? ""; + const layoutDirection = value.length > 20 ? "column" : "row"; return ( - - {key} + + + {key} +