Skip to content

Commit

Permalink
refactor components for improved structure; enhance error handling in…
Browse files Browse the repository at this point in the history
… ErrorBoundary, update navigation styles, and streamline TextField and Select components
  • Loading branch information
actualwitch committed Dec 12, 2024
1 parent 3282d5a commit 0dc94ff
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 76 deletions.
Binary file modified bun.lockb
Binary file not shown.
6 changes: 3 additions & 3 deletions src/components/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useSelectState } from "react-stately";

import { ListBox } from "./ListBox";
import { Popover } from "./Popover";
import { Label, Wrapper } from "./shared";
import { Label, InputContainer } from "./shared";
import { TRIANGLE } from "../const";

interface ButtonProps {
Expand Down Expand Up @@ -51,7 +51,7 @@ export function Select<T extends object>(props: AriaSelectProps<T>) {
const { focusProps, isFocusVisible } = useFocusRing();

return (
<Wrapper>
<InputContainer>
<Label {...labelProps}>{props.label}</Label>
<HiddenSelect state={state} triggerRef={ref} label={props.label} name={props.name} />
<Button {...mergeProps(buttonProps, focusProps)} ref={ref} isOpen={state.isOpen} isFocusVisible={isFocusVisible}>
Expand All @@ -63,6 +63,6 @@ export function Select<T extends object>(props: AriaSelectProps<T>) {
<ListBox {...menuProps} state={state} />
</Popover>
)}
</Wrapper>
</InputContainer>
);
}
43 changes: 32 additions & 11 deletions src/components/Slider.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { useSliderState, type SliderProps } from "react-stately";
import { type SliderProps, useSliderState } from "react-stately";

import { VisuallyHidden, mergeProps, useFocusRing, useNumberFormatter, useSlider, useSliderThumb } from "react-aria";
import { useRef } from "react";
import { css } from "@emotion/react";
import styled from "@emotion/styled";
import { useRef } from "react";
import { VisuallyHidden, mergeProps, useFocusRing, useNumberFormatter, useSlider, useSliderThumb } from "react-aria";
import { TRIANGLE } from "../const";
import { InputContainer } from "./shared";
import { Palette } from "../style/palette";

const Container = styled.div<{ orientation: "horizontal" | "vertical" }>`
display: flex;
${(p) => (p.orientation === "horizontal" ? "flex-direction: column; width: 300px;" : "height: 150px;")}
const Container = styled(InputContainer)<{ orientation: "horizontal" | "vertical" }>`
${(p) => (p.orientation === "horizontal" ? "flex-direction: column;" : "height: 150px;")}
`;

const LabelContainer = styled.div`
Expand All @@ -22,7 +25,7 @@ const Track = styled.div<{ orientation: "horizontal" | "vertical"; disabled?: bo
content: attr(x);
display: block;
position: absolute;
background: gray;
background: white;
height: 3px;
width: 100%;
Expand All @@ -34,10 +37,28 @@ const Track = styled.div<{ orientation: "horizontal" | "vertical"; disabled?: bo
const ThumbComponent = styled.div<{ isFocusVisible?: boolean; isDragging?: boolean }>`
width: 20px;
height: 20px;
border-radius: 50%;
background: gray;
${(p) => (p.isDragging ? "background: dimgray;" : "")}
${(p) => (p.isFocusVisible ? "background: orange;" : "")}
:before {
content: "${TRIANGLE}";
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -10%) rotate(180deg) scale(2);
}
${(p) =>
p.isDragging &&
css`
:before {
color: dimgray;
}
`}
${(p) =>
p.isFocusVisible &&
css`
:before {
color: ${Palette.accent};
}
`}
`;

export function Slider(
Expand Down
7 changes: 4 additions & 3 deletions src/components/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AriaTextFieldProps } from "react-aria";
import { useTextField } from "react-aria";
import styled from "@emotion/styled";
import { withFormStyling, type FormProps } from "../style/form";
import { InputContainer } from "./shared";

const Input = styled.input<FormProps>(withFormStyling);

Expand All @@ -15,8 +16,8 @@ export function TextField(props: AriaTextFieldProps) {
);

return (
<div style={{ display: "flex", flexDirection: "column" }}>
<label {...labelProps}>{label}</label>
<InputContainer>
{label && <label {...labelProps}>{label}</label>}
<Input {...inputProps} ref={ref} />
{props.description && (
<div {...descriptionProps} style={{ fontSize: 12 }}>
Expand All @@ -28,6 +29,6 @@ export function TextField(props: AriaTextFieldProps) {
{validationErrors.join(" ")}
</div>
)}
</div>
</InputContainer>
);
}
19 changes: 12 additions & 7 deletions src/components/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@ export class ErrorBoundary extends Component<Props, { error?: unknown }> {

render() {
if (this.state.error) {
return (<Container><h1>Something went wrong.</h1>
<details>
<summary>Details</summary>
<pre>{String(this.state.error)}</pre>
<pre><code>{JSON.stringify(this.state.error, null, 2)}</code></pre>
</details>
</Container>);
return (
<Container>
<h1>Something went wrong.</h1>
<details>
<summary>Details</summary>
<pre>{String(this.state.error)}</pre>
<pre>
<code>{JSON.stringify(this.state.error, null, 2)}</code>
</pre>
</details>
</Container>
);
}

return this.props.children;
Expand Down
14 changes: 8 additions & 6 deletions src/components/shared.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import styled from "@emotion/styled";

export const Wrapper = styled.div`
display: inline-flex;
flex-direction: column;
position: relative;
`;
import { bs } from "../style";

export const Label = styled.label`
display: block;
text-align: left;
`;

export const InputContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
margin-bottom: ${bs(1 / 2)};
`;
8 changes: 4 additions & 4 deletions src/components/switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ export function Switch<T>({
}: {
value: T;
onChange: (value: T) => void;
children: Array<{ value: T; label: string; isDefault?: true }>;
children: Array<{ value: T; name: string; isDefault?: true }>;
}) {
return (
<Container>
{children.map(({ value: v, label, isDefault }) => (
{children.map(({ value: v, name, isDefault }) => (
<Button
key={label}
key={name}
type="button"
onClick={() => onChange(v)}
disabled={value !== undefined ? v === value : isDefault}
>
{label}
{name}
</Button>
))}
</Container>
Expand Down
6 changes: 6 additions & 0 deletions src/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ const Navigation = styled.nav`
input {
${widthAvailable}
}
a {
text-decoration: none;
&[aria-current="page"] {
text-decoration: underline;
}
}
`;

const GrowBox = styled.div`
Expand Down
57 changes: 39 additions & 18 deletions src/pages/NewExperiment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useHandlers } from "../utils/keyboard";
import { Slider } from "../components/Slider";
import { withDarkMode } from "../style/darkMode";
import { Palette } from "../style/palette";
import { NavLink } from "react-router";

type Provider = "anthropic" | "openai" | "test";

Expand Down Expand Up @@ -127,6 +128,11 @@ export function withIds<T extends string>(items: T[] | readonly T[]) {
export const providerTypes = ["anthropic", "mistral", "openai"] as const;
export type ProviderType = (typeof providerTypes)[number];
export const providers = withIds(providerTypes);
export const providerLabels = {
anthropic: "Anthropic",
mistral: "Mistral",
openai: "OpenAI",
} satisfies { [K in ProviderType]: string };

const ModalContainer = styled.div`
min-height: 80vh;
Expand All @@ -139,10 +145,13 @@ export default function NewExperiment() {
const [selection, setSelection] = useAtom(selectionAtom);
const [message, setMessage] = useState("");
const [role, setRole] = useState<Role>("user");
const [provider, setProvider] = useState<Provider>("anthropic");

const [tokens] = useAtom(tokensAtom);
const tokenProviders = Object.keys(tokens) as ProviderType[];
const providerOptions = withIds(tokenProviders);
const [provider, setProvider] = useState<Provider | null>(tokenProviders[0] ?? null);
const [templates, setTemplates] = useAtom(templatesAtom);
const [temp, setTemp] = useState(0.0);
const [tokens] = useAtom(tokensAtom);

const [object, setObject] = useState<null | object>(null);
useEffect(() => {
Expand All @@ -163,7 +172,7 @@ export default function NewExperiment() {

const isDisabled = role === "tool" && !object;

const [_, runExperiment] = useAtom(actionMap[provider]);
const [_, runExperiment] = useAtom(actionMap[provider ?? "anthropic"]);
const submit = () => {
if (isEditing) {
// const newMessage: Message = { role, content: object || message, fromServer: };
Expand Down Expand Up @@ -209,7 +218,17 @@ export default function NewExperiment() {
setRole("user");
}
}, [experiment, selection, isEditing]);

if (providerOptions.length === 0) {
return (
<Column>
<h2>Welcome to Experiment</h2>
<p>
To start inference, add a token on <NavLink to="/parameters">Parameters</NavLink> page. You can also explore a
.csv file on the <NavLink to="/import">Import</NavLink> page.
</p>
</Column>
);
}
return (
<>
<Column>
Expand Down Expand Up @@ -251,20 +270,22 @@ export default function NewExperiment() {
</Column>
<Sidebar>
<h3>Actions</h3>
<Select
label="Provider"
items={withIds(Object.keys(tokens) as ProviderType[])}
selectedKey={provider}
onSelectionChange={(provider) => {
setProvider(provider);
}}
>
{(item) => (
<Item textValue={item.name}>
<div>{item.name}</div>
</Item>
)}
</Select>
{tokenProviders.length > 1 && (
<Select
label="Provider"
items={providerOptions}
selectedKey={provider}
onSelectionChange={(provider) => {
setProvider(provider);
}}
>
{(item) => (
<Item textValue={item.name}>
<div>{item.name}</div>
</Item>
)}
</Select>
)}
<Slider
value={temp}
onChange={(value: number) => setTemp(value)}
Expand Down
Loading

0 comments on commit 0dc94ff

Please sign in to comment.