refactor: improve UI layout for VS Code and common config options
- Implement horizontal two-column layout for better space utilization - Convert VS Code config from button to checkbox pattern for consistency - Add automatic VS Code settings write on checkbox toggle - Fix layout stability issues with fixed height containers - Remove unnecessary wrapper functions for cleaner code - Adjust spacing and alignment for more compact design
This commit is contained in:
@@ -30,7 +30,7 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
||||
authError,
|
||||
}) => {
|
||||
const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false);
|
||||
const [isWritingVscode, setIsWritingVscode] = useState(false);
|
||||
const [writeVscodeConfig, setWriteVscodeConfig] = useState(false);
|
||||
const [vscodeError, setVscodeError] = useState("");
|
||||
const [vscodeSuccess, setVscodeSuccess] = useState("");
|
||||
|
||||
@@ -48,6 +48,51 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [vscodeSuccess]);
|
||||
|
||||
// 监听 writeVscodeConfig 变化并执行写入
|
||||
useEffect(() => {
|
||||
if (!writeVscodeConfig) return;
|
||||
|
||||
const performWrite = async () => {
|
||||
setVscodeError("");
|
||||
setVscodeSuccess("");
|
||||
|
||||
if (typeof window === "undefined" || !window.api?.writeVscodeSettings) {
|
||||
setVscodeError("当前环境暂不支持写入 VS Code 配置");
|
||||
setWriteVscodeConfig(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const trimmed = configValue.trim();
|
||||
if (!trimmed) {
|
||||
setVscodeError("请先填写 config.toml,再写入 VS Code 配置");
|
||||
setWriteVscodeConfig(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const baseUrl = extractBaseUrlFromToml(trimmed);
|
||||
if (!baseUrl) {
|
||||
setVscodeError("未在 config.toml 中找到 base_url 字段");
|
||||
setWriteVscodeConfig(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const success = await window.api.writeVscodeSettings(baseUrl);
|
||||
if (success) {
|
||||
setVscodeSuccess("已写入 VS Code 配置");
|
||||
} else {
|
||||
setVscodeError("写入 VS Code 配置失败,请稍后重试");
|
||||
setWriteVscodeConfig(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setVscodeError(`写入 VS Code 配置失败: ${String(error)}`);
|
||||
setWriteVscodeConfig(false);
|
||||
}
|
||||
};
|
||||
|
||||
performWrite();
|
||||
}, [writeVscodeConfig, configValue]);
|
||||
|
||||
// 支持按下 ESC 关闭弹窗
|
||||
useEffect(() => {
|
||||
if (!isCommonConfigModalOpen) return;
|
||||
@@ -66,54 +111,6 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
||||
setIsCommonConfigModalOpen(false);
|
||||
};
|
||||
|
||||
const handleAuthChange = (value: string) => {
|
||||
onAuthChange(value);
|
||||
};
|
||||
|
||||
const handleConfigChange = (value: string) => {
|
||||
onConfigChange(value);
|
||||
};
|
||||
|
||||
const handleCommonConfigSnippetChange = (value: string) => {
|
||||
onCommonConfigSnippetChange(value);
|
||||
};
|
||||
|
||||
const handleWriteVscodeConfig = async () => {
|
||||
setVscodeError("");
|
||||
setVscodeSuccess("");
|
||||
|
||||
if (typeof window === "undefined" || !window.api?.writeVscodeSettings) {
|
||||
setVscodeError("当前环境暂不支持写入 VS Code 配置");
|
||||
return;
|
||||
}
|
||||
|
||||
const trimmed = configValue.trim();
|
||||
if (!trimmed) {
|
||||
setVscodeError("请先填写 config.toml,再写入 VS Code 配置");
|
||||
return;
|
||||
}
|
||||
|
||||
const baseUrl = extractBaseUrlFromToml(trimmed);
|
||||
if (!baseUrl) {
|
||||
setVscodeError("未在 config.toml 中找到 base_url 字段");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsWritingVscode(true);
|
||||
try {
|
||||
const success = await window.api.writeVscodeSettings(baseUrl);
|
||||
if (success) {
|
||||
setVscodeSuccess("已写入 VS Code 配置");
|
||||
} else {
|
||||
setVscodeError("写入 VS Code 配置失败,请稍后重试");
|
||||
}
|
||||
} catch (error) {
|
||||
setVscodeError(`写入 VS Code 配置失败: ${String(error)}`);
|
||||
} finally {
|
||||
setIsWritingVscode(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
@@ -126,7 +123,7 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
||||
<textarea
|
||||
id="codexAuth"
|
||||
value={authValue}
|
||||
onChange={(e) => handleAuthChange(e.target.value)}
|
||||
onChange={(e) => onAuthChange(e.target.value)}
|
||||
onBlur={onAuthBlur}
|
||||
placeholder={`{
|
||||
"OPENAI_API_KEY": "sk-your-api-key-here"
|
||||
@@ -155,59 +152,72 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-start justify-between">
|
||||
<label
|
||||
htmlFor="codexConfig"
|
||||
className="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
config.toml (TOML)
|
||||
</label>
|
||||
<label className="inline-flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useCommonConfig}
|
||||
onChange={(e) => onCommonConfigToggle(e.target.checked)}
|
||||
className="w-4 h-4 text-blue-500 bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 rounded focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-2"
|
||||
/>
|
||||
写入通用配置
|
||||
</label>
|
||||
|
||||
{/* 右侧对齐的双列布局 - 使用flex而非grid以更好控制宽度 */}
|
||||
<div className="flex gap-3">
|
||||
{/* 左列:VS Code 配置 */}
|
||||
<div className="flex flex-col items-end space-y-1">
|
||||
<label className="inline-flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={writeVscodeConfig}
|
||||
onChange={(e) => setWriteVscodeConfig(e.target.checked)}
|
||||
className="w-4 h-4 text-blue-500 bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 rounded focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-2"
|
||||
/>
|
||||
写入 VS Code 配置
|
||||
</label>
|
||||
<div className="h-4 w-48 flex justify-end">
|
||||
{vscodeSuccess && !vscodeError && (
|
||||
<p className="text-xs text-emerald-600 dark:text-emerald-400 text-right truncate">
|
||||
{vscodeSuccess}
|
||||
</p>
|
||||
)}
|
||||
{vscodeError && (
|
||||
<p className="text-xs text-red-500 dark:text-red-400 text-right truncate" title={vscodeError}>
|
||||
{vscodeError}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右列:通用配置 - 不设置宽度,让内容自然收缩 */}
|
||||
<div className="flex flex-col items-end space-y-1">
|
||||
<label className="inline-flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useCommonConfig}
|
||||
onChange={(e) => onCommonConfigToggle(e.target.checked)}
|
||||
className="w-4 h-4 text-blue-500 bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 rounded focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-2"
|
||||
/>
|
||||
写入通用配置
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsCommonConfigModalOpen(true)}
|
||||
className="text-xs text-blue-500 dark:text-blue-400 hover:underline whitespace-nowrap"
|
||||
>
|
||||
编辑通用配置
|
||||
</button>
|
||||
{commonConfigError && !isCommonConfigModalOpen && (
|
||||
<p className="text-xs text-red-500 dark:text-red-400 mt-1 max-w-[120px] truncate" title={commonConfigError}>
|
||||
{commonConfigError}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleWriteVscodeConfig}
|
||||
disabled={isWritingVscode}
|
||||
className="text-xs text-blue-500 dark:text-blue-400 hover:underline disabled:opacity-60 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isWritingVscode ? "写入中..." : "写入 VS Code 配置"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsCommonConfigModalOpen(true)}
|
||||
className="text-xs text-blue-500 dark:text-blue-400 hover:underline"
|
||||
>
|
||||
编辑通用配置
|
||||
</button>
|
||||
</div>
|
||||
{commonConfigError && !isCommonConfigModalOpen && (
|
||||
<p className="text-xs text-red-500 dark:text-red-400 text-right">
|
||||
{commonConfigError}
|
||||
</p>
|
||||
)}
|
||||
{vscodeError && (
|
||||
<p className="text-xs text-red-500 dark:text-red-400 text-right">
|
||||
{vscodeError}
|
||||
</p>
|
||||
)}
|
||||
{vscodeSuccess && !vscodeError && (
|
||||
<p className="text-xs text-emerald-600 dark:text-emerald-400 text-right">
|
||||
{vscodeSuccess}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<textarea
|
||||
id="codexConfig"
|
||||
value={configValue}
|
||||
onChange={(e) => handleConfigChange(e.target.value)}
|
||||
onChange={(e) => onConfigChange(e.target.value)}
|
||||
placeholder=""
|
||||
rows={8}
|
||||
className="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 rounded-lg text-sm font-mono focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:focus:ring-blue-400/20 focus:border-blue-500 dark:focus:border-blue-400 transition-colors resize-y min-h-[10rem]"
|
||||
@@ -260,7 +270,7 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
||||
</p>
|
||||
<textarea
|
||||
value={commonConfigSnippet}
|
||||
onChange={(e) => handleCommonConfigSnippetChange(e.target.value)}
|
||||
onChange={(e) => onCommonConfigSnippetChange(e.target.value)}
|
||||
placeholder={`# Common Codex config
|
||||
# Add your common TOML configuration here`}
|
||||
rows={12}
|
||||
|
||||
Reference in New Issue
Block a user