diff --git a/src/games/storywriter/assets/settings-modal.module.css b/src/games/storywriter/assets/settings-modal.module.css index 952f2a4..babe6a1 100644 --- a/src/games/storywriter/assets/settings-modal.module.css +++ b/src/games/storywriter/assets/settings-modal.module.css @@ -111,7 +111,8 @@ } .input, -.select { +.select, +.textarea { width: 100%; padding: 8px; border-radius: 4px; @@ -120,6 +121,13 @@ color: var(--text); } +.textarea { + resize: vertical; + font-family: inherit; + font-size: inherit; + line-height: 1.5; +} + .footer { padding: 16px 20px; border-top: 1px solid var(--border); diff --git a/src/games/storywriter/components/banned-tokens-modal.tsx b/src/games/storywriter/components/banned-tokens-modal.tsx deleted file mode 100644 index 763ba44..0000000 --- a/src/games/storywriter/components/banned-tokens-modal.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import clsx from "clsx"; - -import { useInputState } from "@common/hooks/useInputState"; - -import { useAppState } from "../contexts/state"; -import styles from "../assets/settings-modal.module.css"; -import { X } from "lucide-preact"; - -interface Props { - onClose: () => void; -} - -export const BannedTokensModal = ({ onClose }: Props) => { - const { bannedTokens, dispatch } = useAppState(); - const [inputValue, setInputValue] = useInputState(); - - const handleAdd = () => { - const trimmed = inputValue.trim(); - if (trimmed && !bannedTokens.includes(trimmed)) { - dispatch({ - type: "SET_BANNED_TOKENS", - tokens: [...bannedTokens, trimmed], - }); - setInputValue(""); - } - }; - - const handleRemove = (token: string) => { - dispatch({ - type: "SET_BANNED_TOKENS", - tokens: bannedTokens.filter((t) => t !== token), - }); - }; - - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === "Enter") { - handleAdd(); - } else if (e.key === "Escape") { - onClose(); - } - }; - - const sortedTokens = [...bannedTokens].sort((a, b) => - a.trim().toLowerCase().localeCompare(b.trim().toLowerCase()) - ); - - return ( -
- ); -}; diff --git a/src/games/storywriter/components/connection-settings-modal.tsx b/src/games/storywriter/components/connection-settings-modal.tsx new file mode 100644 index 0000000..5f23328 --- /dev/null +++ b/src/games/storywriter/components/connection-settings-modal.tsx @@ -0,0 +1,185 @@ +import { useMemo, useRef } from "preact/hooks"; + +import { useQuery } from "@common/hooks/useAsyncState"; +import { useInputState } from "@common/hooks/useInputState"; +import { useUpdate } from "@common/hooks/useUpdate"; + +import { useAppState } from "../contexts/state"; +import LLM from "../utils/llm"; +import styles from "../assets/settings-modal.module.css"; +import { X } from "lucide-preact"; + +interface Props { + onClose: () => void; +} + +export const ConnectionSettingsModal = ({ onClose }: Props) => { + const { connection, model, dispatch } = useAppState(); + const [url, setUrl] = useInputState(connection?.url ?? ""); + const [apiKey, setApiKey] = useInputState(connection?.apiKey ?? ""); + const [selectedModel, setSelectedModel] = useInputState(model?.id ?? ""); + const [update, triggerFetch] = useUpdate(); + + const urlRef = useRef(url); + const apiKeyRef = useRef(apiKey); + + urlRef.current = url; + apiKeyRef.current = apiKey; + + const connectionToFetch = useMemoLoading models...
- ) : groupedModels.length > 0 ? ( - + {activeTab === "banned-tokens" ? ( + <> +No banned tokens
) : ( -No models available
- ) - ) : ( -Enter connection details to load models
- )} + sortedTokens.map((token) => ( +