diff --git a/src/components/ConfigExporter.jsx b/src/components/ConfigExporter.jsx
index fbcdd4d..6a588a2 100644
--- a/src/components/ConfigExporter.jsx
+++ b/src/components/ConfigExporter.jsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import toast from 'react-hot-toast';
import { getTokenSupportedModels } from '../api';
@@ -25,6 +25,145 @@ const CCSWITCH_RELEASE_URL =
'https://github.com/farion1231/cc-switch/releases/latest';
const CCSWITCH_REPO_URL = 'https://github.com/farion1231/cc-switch';
+function ThemedSelect({
+ value,
+ onChange,
+ options,
+ placeholder,
+ disabled = false,
+ renderValue,
+ renderOption,
+ emptyLabel,
+}) {
+ const [open, setOpen] = useState(false);
+ const rootRef = useRef(null);
+
+ useEffect(() => {
+ if (disabled) {
+ setOpen(false);
+ }
+ }, [disabled]);
+
+ useEffect(() => {
+ if (!open) return undefined;
+
+ const handlePointerDown = (event) => {
+ if (!rootRef.current?.contains(event.target)) {
+ setOpen(false);
+ }
+ };
+
+ const handleKeyDown = (event) => {
+ if (event.key === 'Escape') {
+ setOpen(false);
+ }
+ };
+
+ document.addEventListener('mousedown', handlePointerDown);
+ document.addEventListener('touchstart', handlePointerDown);
+ document.addEventListener('keydown', handleKeyDown);
+
+ return () => {
+ document.removeEventListener('mousedown', handlePointerDown);
+ document.removeEventListener('touchstart', handlePointerDown);
+ document.removeEventListener('keydown', handleKeyDown);
+ };
+ }, [open]);
+
+ const selectedOption = options.find((option) => option.value === value);
+ const displayLabel = selectedOption
+ ? (renderValue ? renderValue(selectedOption) : selectedOption.label)
+ : placeholder;
+
+ return (
+
+
+
+ {open && (
+
+
+ {options.length > 0 ? (
+ options.map((option) => {
+ const selected = option.value === value;
+ return (
+
+ );
+ })
+ ) : (
+
+ {emptyLabel}
+
+ )}
+
+
+ )}
+
+ );
+}
+
const ConfigExporter = ({ tokens = [] }) => {
const { t } = useTranslation();
const { site } = useSite();
@@ -50,6 +189,23 @@ const ConfigExporter = ({ tokens = [] }) => {
() => TOOLS.find((tool) => tool.id === selectedTool) || TOOLS[0],
[selectedTool],
);
+ const tokenOptions = useMemo(
+ () =>
+ tokens.map((token) => ({
+ value: token.id,
+ label: `${token.name} (sk-${token.key.substring(0, 16)}...)`,
+ token,
+ })),
+ [tokens],
+ );
+ const modelOptions = useMemo(
+ () =>
+ availableModels.map((model) => ({
+ value: model,
+ label: model,
+ })),
+ [availableModels],
+ );
useEffect(() => {
if (tokens.length === 0) {
@@ -449,17 +605,13 @@ print(message.content[0].text)`;
{t('config.selectKey')}
-
+
@@ -479,28 +631,22 @@ print(message.content[0].text)`;
{t('config.selectModel')}
-
+ onChange={setSelectedModel}
+ options={modelOptions}
+ placeholder={
+ loadingModels
+ ? t('config.loadingModels')
+ : t('config.selectModel')
+ }
+ disabled={loadingModels || modelOptions.length === 0}
+ emptyLabel={
+ modelsError
+ ? t('tokens.loadSupportedModelsFailed')
+ : t('tokens.noSupportedModels')
+ }
+ />