From b2469df545107eee65de0b51b458a158c034dd07 Mon Sep 17 00:00:00 2001 From: Markus Ecker Date: Thu, 16 Nov 2023 22:26:20 +0100 Subject: [PATCH] Unify contexts #4 --- packages/css/Assistant.css | 16 +++++++ packages/css/Theme.css | 18 -------- packages/react/src/Assistant.tsx | 34 +++++++++++---- packages/react/src/Beak.tsx | 31 ++++++++++---- packages/react/src/Button.tsx | 4 +- packages/react/src/Header.tsx | 4 +- packages/react/src/Input.tsx | 6 +-- packages/react/src/Messages.tsx | 6 +-- packages/react/src/Theme.tsx | 72 -------------------------------- 9 files changed, 73 insertions(+), 118 deletions(-) delete mode 100644 packages/css/Theme.css delete mode 100644 packages/react/src/Theme.tsx diff --git a/packages/css/Assistant.css b/packages/css/Assistant.css index 6883269..8843ba0 100644 --- a/packages/css/Assistant.css +++ b/packages/css/Assistant.css @@ -3,4 +3,20 @@ bottom: 1rem; right: 1rem; z-index: 30; + line-height: 1.5; + -webkit-text-size-adjust: 100%; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-feature-settings: normal; + font-variation-settings: normal; + touch-action: manipulation; +} + +.beakAssistantWindow svg { + display: inline-block; + vertical-align: middle; } diff --git a/packages/css/Theme.css b/packages/css/Theme.css deleted file mode 100644 index 4868b24..0000000 --- a/packages/css/Theme.css +++ /dev/null @@ -1,18 +0,0 @@ -.beakTheme { - line-height: 1.5; - -webkit-text-size-adjust: 100%; - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, - "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, - "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-feature-settings: normal; - font-variation-settings: normal; - touch-action: manipulation; -} - -.beakTheme svg { - display: inline-block; - vertical-align: middle; -} diff --git a/packages/react/src/Assistant.tsx b/packages/react/src/Assistant.tsx index 637f25b..eb31c78 100644 --- a/packages/react/src/Assistant.tsx +++ b/packages/react/src/Assistant.tsx @@ -1,7 +1,11 @@ import React, { useCallback, useEffect, useMemo } from "react"; -import { useBeakContext } from "./Beak"; +import { + BeakColorScheme, + BeakContext, + BeakIcons, + useBeakContext, +} from "./Beak"; import { Message } from "@beakjs/core"; -import { ColorScheme, Icons, Theme } from "./Theme"; import { ButtonProps, HeaderProps, @@ -21,8 +25,8 @@ interface AssistantWindowProps { clickOutsideToClose?: boolean; hitEscapeToClose?: boolean; hotkey?: string; - icons?: Required; - colorScheme?: ColorScheme; + icons?: Required; + colorScheme?: BeakColorScheme; Window?: React.ComponentType; Button?: React.ComponentType; Header?: React.ComponentType; @@ -68,9 +72,25 @@ export const AssistantWindow: React.FC = ({ await beak.runChatCompletion(message); }; + const ctx = useMemo(() => { + return { + ...context, + icons: { + ...context.icons, + ...icons, + }, + colorScheme: colorScheme || context.colorScheme, + }; + }, [context, icons, colorScheme]); + + const colorSchemeClass = + "beakColorScheme" + + ctx.colorScheme[0].toUpperCase() + + ctx.colorScheme.slice(1); + return ( - -
+ +
= ({
- +
); }; diff --git a/packages/react/src/Beak.tsx b/packages/react/src/Beak.tsx index 844208a..04794e5 100644 --- a/packages/react/src/Beak.tsx +++ b/packages/react/src/Beak.tsx @@ -1,8 +1,20 @@ import React, { useMemo } from "react"; import { BeakCore, OpenAIModel, DebugLogger } from "@beakjs/core"; +import * as DefaultIcons from "./Icons"; const DEFAULT_DEBUG_LOGGER = new DebugLogger([]); +export type BeakColorScheme = "auto" | "light" | "dark"; + +export interface BeakIcons { + openIcon?: React.ReactNode; + closeIcon?: React.ReactNode; + headerCloseIcon?: React.ReactNode; + sendIcon?: React.ReactNode; + activityIcon?: React.ReactNode; + spinnerIcon?: React.ReactNode; +} + interface BeakLabels { initial?: string | string[]; title?: string; @@ -15,8 +27,9 @@ interface BeakLabels { interface BeakContext { beak: BeakCore; labels: Required; + icons: Required; + colorScheme: BeakColorScheme; debugLogger: DebugLogger; - theme: BeakTheme; } export const BeakContext = React.createContext( @@ -33,8 +46,6 @@ export function useBeakContext(): BeakContext { return context; } -type BeakTheme = "auto" | "light" | "dark"; - interface BeakProps { openAIApiKey: string; openAIModel?: OpenAIModel; @@ -43,7 +54,6 @@ interface BeakProps { maxFeedback?: number; labels?: BeakLabels; debugLogger?: DebugLogger; - theme?: BeakTheme; children?: React.ReactNode; } @@ -55,7 +65,6 @@ export const Beak: React.FC = ({ maxFeedback, labels, debugLogger, - theme, children, }) => { const beak = useMemo( @@ -93,9 +102,17 @@ export const Beak: React.FC = ({ }, debugLogger: debugLogger || DEFAULT_DEBUG_LOGGER, - theme: theme || "auto", + colorScheme: "auto" as BeakColorScheme, + icons: { + openIcon: DefaultIcons.OpenIcon, + closeIcon: DefaultIcons.CloseIcon, + headerCloseIcon: DefaultIcons.HeaderCloseIcon, + sendIcon: DefaultIcons.SendIcon, + activityIcon: DefaultIcons.ActivityIcon, + spinnerIcon: DefaultIcons.SpinnerIcon, + }, }), - [labels, debugLogger, theme] + [labels, debugLogger] ); return ( {children} diff --git a/packages/react/src/Button.tsx b/packages/react/src/Button.tsx index b4b6aea..b80efbf 100644 --- a/packages/react/src/Button.tsx +++ b/packages/react/src/Button.tsx @@ -1,10 +1,10 @@ import React from "react"; import { ButtonProps } from "./props"; +import { useBeakContext } from "./Beak"; import "../../css/Button.css"; -import { useBeakThemeContext } from "./Theme"; export const Button: React.FC = ({ open, setOpen }) => { - const context = useBeakThemeContext(); + const context = useBeakContext(); // To ensure that the mouse handler fires even when the button is scaled down // we wrap the button in a div and attach the handler to the div return ( diff --git a/packages/react/src/Header.tsx b/packages/react/src/Header.tsx index 6ada5a3..ab2d875 100644 --- a/packages/react/src/Header.tsx +++ b/packages/react/src/Header.tsx @@ -1,18 +1,16 @@ import React from "react"; import { HeaderProps } from "./props"; import "../../css/Header.css"; -import { useBeakThemeContext } from "./Theme"; import { useBeakContext } from "./Beak"; export const Header: React.FC = ({ setOpen }) => { const context = useBeakContext(); - const themeContext = useBeakThemeContext(); return (
{context.labels.title}
); diff --git a/packages/react/src/Input.tsx b/packages/react/src/Input.tsx index 15e9ba8..c0c3c79 100644 --- a/packages/react/src/Input.tsx +++ b/packages/react/src/Input.tsx @@ -2,12 +2,10 @@ import React, { useRef, useState } from "react"; import TextareaAutosize from "react-textarea-autosize"; import { InputProps } from "./props"; import { useBeakContext } from "./Beak"; -import { useBeakThemeContext } from "./Theme"; import "../../css/Input.css"; export const Input: React.FC = ({ inProgress, onSend }) => { const context = useBeakContext(); - const themeContext = useBeakThemeContext(); const textareaRef = useRef(null); const handleDivClick = (event: React.MouseEvent) => { @@ -26,9 +24,7 @@ export const Input: React.FC = ({ inProgress, onSend }) => { textareaRef.current?.focus(); }; - const icon = inProgress - ? themeContext.icons.activityIcon - : themeContext.icons.sendIcon; + const icon = inProgress ? context.icons.activityIcon : context.icons.sendIcon; const disabled = inProgress || text.length === 0; return ( diff --git a/packages/react/src/Messages.tsx b/packages/react/src/Messages.tsx index 358a681..5ec0e4e 100644 --- a/packages/react/src/Messages.tsx +++ b/packages/react/src/Messages.tsx @@ -1,12 +1,10 @@ import React, { useEffect } from "react"; import { MessagesProps } from "./props"; import { useBeakContext } from "./Beak"; -import { useBeakThemeContext } from "./Theme"; import "../../css/Messages.css"; export const Messages: React.FC = ({ messages }) => { const context = useBeakContext(); - const themeContext = useBeakThemeContext(); const messagesEndRef = React.useRef(null); const scrollToBottom = () => { @@ -34,13 +32,13 @@ export const Messages: React.FC = ({ messages }) => { if (message.status === "pending" && !message.content) { return (
- {themeContext.icons.spinnerIcon} + {context.icons.spinnerIcon}
); } else if (message.status === "partial") { return (
- {context.labels.thinking} {themeContext.icons.spinnerIcon} + {context.labels.thinking} {context.icons.spinnerIcon}
); } else if (message.content) { diff --git a/packages/react/src/Theme.tsx b/packages/react/src/Theme.tsx deleted file mode 100644 index 8ee6bc0..0000000 --- a/packages/react/src/Theme.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from "react"; -import * as DefaultIcons from "./Icons"; -import "../../css/Theme.css"; - -export type ColorScheme = "auto" | "light" | "dark"; - -export interface Icons { - openIcon?: React.ReactNode; - closeIcon?: React.ReactNode; - headerCloseIcon?: React.ReactNode; - sendIcon?: React.ReactNode; - activityIcon?: React.ReactNode; - spinnerIcon?: React.ReactNode; -} - -interface ThemeContext { - colorScheme: ColorScheme; - icons: Required; -} - -export const ThemeContext = React.createContext( - undefined -); - -export function useBeakThemeContext(): ThemeContext { - const context = React.useContext(ThemeContext); - if (context === undefined) { - throw new Error( - "Theme not found. Did you forget to wrap your app in a component?" - ); - } - return context; -} - -interface ThemeProps { - colorScheme?: ColorScheme; - icons?: Icons; - children?: React.ReactNode; -} - -export function Theme({ - colorScheme = "auto", - icons, - children, -}: ThemeProps): JSX.Element { - const theme = React.useMemo(() => { - return { - colorScheme: colorScheme, - icons: { - ...{ - openIcon: DefaultIcons.OpenIcon, - closeIcon: DefaultIcons.CloseIcon, - headerCloseIcon: DefaultIcons.HeaderCloseIcon, - sendIcon: DefaultIcons.SendIcon, - activityIcon: DefaultIcons.ActivityIcon, - spinnerIcon: DefaultIcons.SpinnerIcon, - }, - ...icons, - }, - }; - }, [colorScheme, icons]); - - return ( -
- {children} -
- ); -}