Commit d60ecae9 authored by edy's avatar edy

feat(settings): edit model endpoint configs

parent c591100b
...@@ -223,19 +223,19 @@ function mergeExpertModelConfig( ...@@ -223,19 +223,19 @@ function mergeExpertModelConfig(
): ExpertModelConfig { ): ExpertModelConfig {
return { return {
image: { image: {
baseUrl: FIXED_EXPERT_MODEL_ENDPOINTS.image.baseUrl, baseUrl: typeof input?.image?.baseUrl === "string" ? input.image.baseUrl.trim() : current.image.baseUrl,
apiKeyConfigured: typeof input?.image?.apiKey === "string" ? Boolean(input.image.apiKey.trim()) : current.image.apiKeyConfigured, apiKeyConfigured: typeof input?.image?.apiKey === "string" ? Boolean(input.image.apiKey.trim()) : current.image.apiKeyConfigured,
modelId: FIXED_EXPERT_MODEL_ENDPOINTS.image.modelId modelId: typeof input?.image?.modelId === "string" ? input.image.modelId.trim() : current.image.modelId
}, },
video: { video: {
baseUrl: FIXED_EXPERT_MODEL_ENDPOINTS.video.baseUrl, baseUrl: typeof input?.video?.baseUrl === "string" ? input.video.baseUrl.trim() : current.video.baseUrl,
apiKeyConfigured: typeof input?.video?.apiKey === "string" ? Boolean(input.video.apiKey.trim()) : current.video.apiKeyConfigured, apiKeyConfigured: typeof input?.video?.apiKey === "string" ? Boolean(input.video.apiKey.trim()) : current.video.apiKeyConfigured,
modelId: FIXED_EXPERT_MODEL_ENDPOINTS.video.modelId modelId: typeof input?.video?.modelId === "string" ? input.video.modelId.trim() : current.video.modelId
}, },
copywriting: { copywriting: {
baseUrl: FIXED_EXPERT_MODEL_ENDPOINTS.copywriting.baseUrl, baseUrl: typeof input?.copywriting?.baseUrl === "string" ? input.copywriting.baseUrl.trim() : current.copywriting.baseUrl,
apiKeyConfigured: typeof input?.copywriting?.apiKey === "string" ? Boolean(input.copywriting.apiKey.trim()) : current.copywriting.apiKeyConfigured, apiKeyConfigured: typeof input?.copywriting?.apiKey === "string" ? Boolean(input.copywriting.apiKey.trim()) : current.copywriting.apiKeyConfigured,
modelId: FIXED_EXPERT_MODEL_ENDPOINTS.copywriting.modelId modelId: typeof input?.copywriting?.modelId === "string" ? input.copywriting.modelId.trim() : current.copywriting.modelId
}, },
digitalHuman: { digitalHuman: {
...FIXED_DIGITAL_HUMAN_CONFIG, ...FIXED_DIGITAL_HUMAN_CONFIG,
...@@ -395,19 +395,25 @@ export class AppConfigService { ...@@ -395,19 +395,25 @@ export class AppConfigService {
runtimeMode: normalizeRuntimeMode(config.runtimeMode ?? process.env.QJCLAW_RUNTIME_MODE), runtimeMode: normalizeRuntimeMode(config.runtimeMode ?? process.env.QJCLAW_RUNTIME_MODE),
expertModelConfig: { expertModelConfig: {
image: { image: {
baseUrl: defaultExpertModelConfig.image.baseUrl, baseUrl: typeof config.expertModelConfig?.image?.baseUrl === "string"
? config.expertModelConfig.image.baseUrl
: defaultExpertModelConfig.image.baseUrl,
apiKeyConfigured: Boolean(config.expertModelConfig?.image?.apiKeyConfigured), apiKeyConfigured: Boolean(config.expertModelConfig?.image?.apiKeyConfigured),
modelId: defaultExpertModelConfig.image.modelId modelId: config.expertModelConfig?.image?.modelId ?? defaultExpertModelConfig.image.modelId
}, },
video: { video: {
baseUrl: defaultExpertModelConfig.video.baseUrl, baseUrl: typeof config.expertModelConfig?.video?.baseUrl === "string"
? config.expertModelConfig.video.baseUrl
: defaultExpertModelConfig.video.baseUrl,
apiKeyConfigured: Boolean(config.expertModelConfig?.video?.apiKeyConfigured), apiKeyConfigured: Boolean(config.expertModelConfig?.video?.apiKeyConfigured),
modelId: defaultExpertModelConfig.video.modelId modelId: config.expertModelConfig?.video?.modelId ?? defaultExpertModelConfig.video.modelId
}, },
copywriting: { copywriting: {
baseUrl: defaultExpertModelConfig.copywriting.baseUrl, baseUrl: typeof config.expertModelConfig?.copywriting?.baseUrl === "string"
? config.expertModelConfig.copywriting.baseUrl
: defaultExpertModelConfig.copywriting.baseUrl,
apiKeyConfigured: Boolean(config.expertModelConfig?.copywriting?.apiKeyConfigured), apiKeyConfigured: Boolean(config.expertModelConfig?.copywriting?.apiKeyConfigured),
modelId: defaultExpertModelConfig.copywriting.modelId modelId: config.expertModelConfig?.copywriting?.modelId ?? defaultExpertModelConfig.copywriting.modelId
}, },
digitalHuman: { digitalHuman: {
...FIXED_DIGITAL_HUMAN_CONFIG, ...FIXED_DIGITAL_HUMAN_CONFIG,
......
...@@ -366,10 +366,22 @@ export default function App() { ...@@ -366,10 +366,22 @@ export default function App() {
setWorkspacePathDraft, setWorkspacePathDraft,
imageModelApiKeyDraft, imageModelApiKeyDraft,
setImageModelApiKeyDraft, setImageModelApiKeyDraft,
imageModelBaseUrlDraft,
setImageModelBaseUrlDraft,
imageModelModelIdDraft,
setImageModelModelIdDraft,
videoModelApiKeyDraft, videoModelApiKeyDraft,
setVideoModelApiKeyDraft, setVideoModelApiKeyDraft,
videoModelBaseUrlDraft,
setVideoModelBaseUrlDraft,
videoModelModelIdDraft,
setVideoModelModelIdDraft,
copywritingModelApiKeyDraft, copywritingModelApiKeyDraft,
setCopywritingModelApiKeyDraft, setCopywritingModelApiKeyDraft,
copywritingModelBaseUrlDraft,
setCopywritingModelBaseUrlDraft,
copywritingModelModelIdDraft,
setCopywritingModelModelIdDraft,
digitalHumanVolcAccessKeyDraft, digitalHumanVolcAccessKeyDraft,
setDigitalHumanVolcAccessKeyDraft, setDigitalHumanVolcAccessKeyDraft,
digitalHumanVolcSecretKeyDraft, digitalHumanVolcSecretKeyDraft,
...@@ -436,8 +448,14 @@ export default function App() { ...@@ -436,8 +448,14 @@ export default function App() {
lobsterKey: lobsterKeyDraft, lobsterKey: lobsterKeyDraft,
workspacePath: workspacePathDraft, workspacePath: workspacePathDraft,
imageModelApiKey: imageModelApiKeyDraft, imageModelApiKey: imageModelApiKeyDraft,
imageModelBaseUrl: imageModelBaseUrlDraft,
imageModelModelId: imageModelModelIdDraft,
videoModelApiKey: videoModelApiKeyDraft, videoModelApiKey: videoModelApiKeyDraft,
videoModelBaseUrl: videoModelBaseUrlDraft,
videoModelModelId: videoModelModelIdDraft,
copywritingModelApiKey: copywritingModelApiKeyDraft, copywritingModelApiKey: copywritingModelApiKeyDraft,
copywritingModelBaseUrl: copywritingModelBaseUrlDraft,
copywritingModelModelId: copywritingModelModelIdDraft,
digitalHumanVolcAccessKey: digitalHumanVolcAccessKeyDraft, digitalHumanVolcAccessKey: digitalHumanVolcAccessKeyDraft,
digitalHumanVolcSecretKey: digitalHumanVolcSecretKeyDraft, digitalHumanVolcSecretKey: digitalHumanVolcSecretKeyDraft,
digitalHumanQiniuAccessKey: digitalHumanQiniuAccessKeyDraft, digitalHumanQiniuAccessKey: digitalHumanQiniuAccessKeyDraft,
...@@ -464,14 +482,26 @@ export default function App() { ...@@ -464,14 +482,26 @@ export default function App() {
setWorkspacePathDraft, setWorkspacePathDraft,
setLobsterKeyDraft, setLobsterKeyDraft,
setImageModelApiKeyDraft, setImageModelApiKeyDraft,
setImageModelBaseUrlDraft,
setImageModelModelIdDraft,
setVideoModelApiKeyDraft, setVideoModelApiKeyDraft,
setVideoModelBaseUrlDraft,
setVideoModelModelIdDraft,
setCopywritingModelApiKeyDraft, setCopywritingModelApiKeyDraft,
setCopywritingModelBaseUrlDraft,
setCopywritingModelModelIdDraft,
setDigitalHumanVolcAccessKeyDraft, setDigitalHumanVolcAccessKeyDraft,
setDigitalHumanVolcSecretKeyDraft, setDigitalHumanVolcSecretKeyDraft,
setDigitalHumanQiniuAccessKeyDraft, setDigitalHumanQiniuAccessKeyDraft,
setDigitalHumanQiniuSecretKeyDraft, setDigitalHumanQiniuSecretKeyDraft,
setVideoAnalyzerBaseUrlDraft,
setVideoAnalyzerModelIdDraft,
setVideoAnalyzerApiKeyDraft, setVideoAnalyzerApiKeyDraft,
setReplicationBriefBaseUrlDraft,
setReplicationBriefModelIdDraft,
setReplicationBriefApiKeyDraft, setReplicationBriefApiKeyDraft,
setVectcutBaseUrlDraft,
setVectcutFileBaseUrlDraft,
setVectcutApiKeyDraft, setVectcutApiKeyDraft,
setXhsFeishuAppIdDraft, setXhsFeishuAppIdDraft,
setXhsFeishuAppSecretDraft, setXhsFeishuAppSecretDraft,
...@@ -506,9 +536,21 @@ export default function App() { ...@@ -506,9 +536,21 @@ export default function App() {
const displayedWorkspacePath = workspacePathDraft.trim() || savedWorkspacePath || ui.none; const displayedWorkspacePath = workspacePathDraft.trim() || savedWorkspacePath || ui.none;
const hasPendingWorkspacePathChange = Boolean(config && workspacePathDraft.trim() && workspacePathDraft.trim() !== savedWorkspacePath); const hasPendingWorkspacePathChange = Boolean(config && workspacePathDraft.trim() && workspacePathDraft.trim() !== savedWorkspacePath);
const hasPendingBasicConfig = hasPendingLobsterKey || hasPendingWorkspacePathChange; const hasPendingBasicConfig = hasPendingLobsterKey || hasPendingWorkspacePathChange;
const hasPendingCopywritingConfig = Boolean(copywritingModelApiKeyDraft.trim()); const hasPendingCopywritingConfig = Boolean(
const hasPendingImageConfig = Boolean(imageModelApiKeyDraft.trim()); copywritingModelBaseUrlDraft.trim() !== (config?.expertModelConfig.copywriting.baseUrl ?? "").trim()
const hasPendingVideoConfig = Boolean(videoModelApiKeyDraft.trim()); || copywritingModelModelIdDraft.trim() !== (config?.expertModelConfig.copywriting.modelId ?? "").trim()
|| copywritingModelApiKeyDraft.trim()
);
const hasPendingImageConfig = Boolean(
imageModelBaseUrlDraft.trim() !== (config?.expertModelConfig.image.baseUrl ?? "").trim()
|| imageModelModelIdDraft.trim() !== (config?.expertModelConfig.image.modelId ?? "").trim()
|| imageModelApiKeyDraft.trim()
);
const hasPendingVideoConfig = Boolean(
videoModelBaseUrlDraft.trim() !== (config?.expertModelConfig.video.baseUrl ?? "").trim()
|| videoModelModelIdDraft.trim() !== (config?.expertModelConfig.video.modelId ?? "").trim()
|| videoModelApiKeyDraft.trim()
);
const hasPendingDigitalHumanConfig = Boolean( const hasPendingDigitalHumanConfig = Boolean(
digitalHumanVolcAccessKeyDraft.trim() digitalHumanVolcAccessKeyDraft.trim()
|| digitalHumanVolcSecretKeyDraft.trim() || digitalHumanVolcSecretKeyDraft.trim()
...@@ -544,14 +586,35 @@ export default function App() { ...@@ -544,14 +586,35 @@ export default function App() {
setXhsFeishuTableIdDraft(drafts.xhsFeishuTableId); setXhsFeishuTableIdDraft(drafts.xhsFeishuTableId);
}, []); }, []);
const resetCopywritingSettingsDrafts = useCallback(() => { const resetCopywritingSettingsDrafts = useCallback(() => {
setCopywritingModelApiKeyDraft(getResetCopywritingSettingsDrafts().copywritingModelApiKey); if (!config) {
}, []); return;
}
const drafts = getResetCopywritingSettingsDrafts(config);
setCopywritingModelBaseUrlDraft(drafts.copywritingModelBaseUrl);
setCopywritingModelModelIdDraft(drafts.copywritingModelModelId);
setCopywritingModelApiKeyDraft(drafts.copywritingModelApiKey);
}, [config]);
const resetImageSettingsDrafts = useCallback(() => { const resetImageSettingsDrafts = useCallback(() => {
setImageModelApiKeyDraft(getResetImageSettingsDrafts().imageModelApiKey); if (!config) {
}, []); return;
}
const drafts = getResetImageSettingsDrafts(config);
setImageModelBaseUrlDraft(drafts.imageModelBaseUrl);
setImageModelModelIdDraft(drafts.imageModelModelId);
setImageModelApiKeyDraft(drafts.imageModelApiKey);
}, [config]);
const resetVideoSettingsDrafts = useCallback(() => { const resetVideoSettingsDrafts = useCallback(() => {
setVideoModelApiKeyDraft(getResetVideoSettingsDrafts().videoModelApiKey); if (!config) {
}, []); return;
}
const drafts = getResetVideoSettingsDrafts(config);
setVideoModelBaseUrlDraft(drafts.videoModelBaseUrl);
setVideoModelModelIdDraft(drafts.videoModelModelId);
setVideoModelApiKeyDraft(drafts.videoModelApiKey);
}, [config]);
const resetDigitalHumanSettingsDrafts = useCallback(() => { const resetDigitalHumanSettingsDrafts = useCallback(() => {
const drafts = getResetDigitalHumanSettingsDrafts(); const drafts = getResetDigitalHumanSettingsDrafts();
setDigitalHumanVolcAccessKeyDraft(drafts.digitalHumanVolcAccessKey); setDigitalHumanVolcAccessKeyDraft(drafts.digitalHumanVolcAccessKey);
...@@ -1419,8 +1482,14 @@ export default function App() { ...@@ -1419,8 +1482,14 @@ export default function App() {
xhsFeishuAppSecret: xhsFeishuAppSecretDraft, xhsFeishuAppSecret: xhsFeishuAppSecretDraft,
xhsFeishuAppToken: xhsFeishuAppTokenDraft, xhsFeishuAppToken: xhsFeishuAppTokenDraft,
xhsFeishuTableId: xhsFeishuTableIdDraft, xhsFeishuTableId: xhsFeishuTableIdDraft,
copywritingModelBaseUrl: copywritingModelBaseUrlDraft,
copywritingModelModelId: copywritingModelModelIdDraft,
copywritingModelApiKey: copywritingModelApiKeyDraft, copywritingModelApiKey: copywritingModelApiKeyDraft,
imageModelBaseUrl: imageModelBaseUrlDraft,
imageModelModelId: imageModelModelIdDraft,
imageModelApiKey: imageModelApiKeyDraft, imageModelApiKey: imageModelApiKeyDraft,
videoModelBaseUrl: videoModelBaseUrlDraft,
videoModelModelId: videoModelModelIdDraft,
videoModelApiKey: videoModelApiKeyDraft, videoModelApiKey: videoModelApiKeyDraft,
digitalHumanVolcAccessKey: digitalHumanVolcAccessKeyDraft, digitalHumanVolcAccessKey: digitalHumanVolcAccessKeyDraft,
digitalHumanVolcSecretKey: digitalHumanVolcSecretKeyDraft, digitalHumanVolcSecretKey: digitalHumanVolcSecretKeyDraft,
...@@ -1442,8 +1511,14 @@ export default function App() { ...@@ -1442,8 +1511,14 @@ export default function App() {
setXhsFeishuAppSecret: setXhsFeishuAppSecretDraft, setXhsFeishuAppSecret: setXhsFeishuAppSecretDraft,
setXhsFeishuAppToken: setXhsFeishuAppTokenDraft, setXhsFeishuAppToken: setXhsFeishuAppTokenDraft,
setXhsFeishuTableId: setXhsFeishuTableIdDraft, setXhsFeishuTableId: setXhsFeishuTableIdDraft,
setCopywritingModelBaseUrl: setCopywritingModelBaseUrlDraft,
setCopywritingModelModelId: setCopywritingModelModelIdDraft,
setCopywritingModelApiKey: setCopywritingModelApiKeyDraft, setCopywritingModelApiKey: setCopywritingModelApiKeyDraft,
setImageModelBaseUrl: setImageModelBaseUrlDraft,
setImageModelModelId: setImageModelModelIdDraft,
setImageModelApiKey: setImageModelApiKeyDraft, setImageModelApiKey: setImageModelApiKeyDraft,
setVideoModelBaseUrl: setVideoModelBaseUrlDraft,
setVideoModelModelId: setVideoModelModelIdDraft,
setVideoModelApiKey: setVideoModelApiKeyDraft, setVideoModelApiKey: setVideoModelApiKeyDraft,
setDigitalHumanVolcAccessKey: setDigitalHumanVolcAccessKeyDraft, setDigitalHumanVolcAccessKey: setDigitalHumanVolcAccessKeyDraft,
setDigitalHumanVolcSecretKey: setDigitalHumanVolcSecretKeyDraft, setDigitalHumanVolcSecretKey: setDigitalHumanVolcSecretKeyDraft,
...@@ -1459,18 +1534,18 @@ export default function App() { ...@@ -1459,18 +1534,18 @@ export default function App() {
setVectcutFileBaseUrl: setVectcutFileBaseUrlDraft, setVectcutFileBaseUrl: setVectcutFileBaseUrlDraft,
setVectcutApiKey: setVectcutApiKeyDraft setVectcutApiKey: setVectcutApiKeyDraft
}, },
onSaveLobsterKey: () => void saveLobsterKey(), onSaveLobsterKey: saveLobsterKey,
onSaveXhsFeishuConfig: () => void saveXhsFeishuConfig(), onSaveXhsFeishuConfig: saveXhsFeishuConfig,
onResetXhsFeishuConfig: resetXhsFeishuSettingsDrafts, onResetXhsFeishuConfig: resetXhsFeishuSettingsDrafts,
onSaveCopywritingConfig: () => void saveCopywritingConfig(), onSaveCopywritingConfig: saveCopywritingConfig,
onResetCopywritingConfig: resetCopywritingSettingsDrafts, onResetCopywritingConfig: resetCopywritingSettingsDrafts,
onSaveImageConfig: () => void saveImageConfig(), onSaveImageConfig: saveImageConfig,
onResetImageConfig: resetImageSettingsDrafts, onResetImageConfig: resetImageSettingsDrafts,
onSaveVideoConfig: () => void saveVideoConfig(), onSaveVideoConfig: saveVideoConfig,
onResetVideoConfig: resetVideoSettingsDrafts, onResetVideoConfig: resetVideoSettingsDrafts,
onSaveDigitalHumanConfig: () => void saveDigitalHumanConfig(), onSaveDigitalHumanConfig: saveDigitalHumanConfig,
onResetDigitalHumanConfig: resetDigitalHumanSettingsDrafts, onResetDigitalHumanConfig: resetDigitalHumanSettingsDrafts,
onSaveDouyinRuntimeConfig: () => void saveDouyinRuntimeConfig(), onSaveDouyinRuntimeConfig: saveDouyinRuntimeConfig,
onResetDouyinRuntimeConfig: resetDouyinRuntimeSettingsDrafts, onResetDouyinRuntimeConfig: resetDouyinRuntimeSettingsDrafts,
onRevealSecret: revealConfigSecret, onRevealSecret: revealConfigSecret,
pickWorkspaceDirectory, pickWorkspaceDirectory,
......
...@@ -3,7 +3,7 @@ import type { AppConfig, ConfigSecretId } from "@qjclaw/shared-types" ...@@ -3,7 +3,7 @@ import type { AppConfig, ConfigSecretId } from "@qjclaw/shared-types"
import { Tabs, type TabItem } from "../../components/ui/Tabs" import { Tabs, type TabItem } from "../../components/ui/Tabs"
type SetDraft = (value: string) => void type SetDraft = (value: string) => void
type SettingsActionHandler = () => void | Promise<void> type SettingsActionHandler = () => void | boolean | Promise<void | boolean>
type SecretRevealState = Partial<Record<ConfigSecretId, boolean>> type SecretRevealState = Partial<Record<ConfigSecretId, boolean>>
type SecretValueState = Partial<Record<ConfigSecretId, string>> type SecretValueState = Partial<Record<ConfigSecretId, string>>
...@@ -13,8 +13,14 @@ interface SettingsDrafts { ...@@ -13,8 +13,14 @@ interface SettingsDrafts {
xhsFeishuAppSecret: string xhsFeishuAppSecret: string
xhsFeishuAppToken: string xhsFeishuAppToken: string
xhsFeishuTableId: string xhsFeishuTableId: string
copywritingModelBaseUrl: string
copywritingModelModelId: string
copywritingModelApiKey: string copywritingModelApiKey: string
imageModelBaseUrl: string
imageModelModelId: string
imageModelApiKey: string imageModelApiKey: string
videoModelBaseUrl: string
videoModelModelId: string
videoModelApiKey: string videoModelApiKey: string
digitalHumanVolcAccessKey: string digitalHumanVolcAccessKey: string
digitalHumanVolcSecretKey: string digitalHumanVolcSecretKey: string
...@@ -37,8 +43,14 @@ interface SettingsDraftSetters { ...@@ -37,8 +43,14 @@ interface SettingsDraftSetters {
setXhsFeishuAppSecret: SetDraft setXhsFeishuAppSecret: SetDraft
setXhsFeishuAppToken: SetDraft setXhsFeishuAppToken: SetDraft
setXhsFeishuTableId: SetDraft setXhsFeishuTableId: SetDraft
setCopywritingModelBaseUrl: SetDraft
setCopywritingModelModelId: SetDraft
setCopywritingModelApiKey: SetDraft setCopywritingModelApiKey: SetDraft
setImageModelBaseUrl: SetDraft
setImageModelModelId: SetDraft
setImageModelApiKey: SetDraft setImageModelApiKey: SetDraft
setVideoModelBaseUrl: SetDraft
setVideoModelModelId: SetDraft
setVideoModelApiKey: SetDraft setVideoModelApiKey: SetDraft
setDigitalHumanVolcAccessKey: SetDraft setDigitalHumanVolcAccessKey: SetDraft
setDigitalHumanVolcSecretKey: SetDraft setDigitalHumanVolcSecretKey: SetDraft
...@@ -101,7 +113,9 @@ interface SectionActions { ...@@ -101,7 +113,9 @@ interface SectionActions {
} }
type SettingsTabId = "basic" | "xhsFeishu" | "copywriting" | "image" | "video" | "digitalHuman" | "douyinRuntime" type SettingsTabId = "basic" | "xhsFeishu" | "copywriting" | "image" | "video" | "digitalHuman" | "douyinRuntime"
type EditableSettingsSectionId = "copywriting" | "image" | "video" | "douyinRuntime"
type SettingsConfigSource = "cloud" | "local" type SettingsConfigSource = "cloud" | "local"
type EditingSettingsSections = Partial<Record<EditableSettingsSectionId, boolean>>
const settingsTabs: TabItem[] = [ const settingsTabs: TabItem[] = [
{ id: "basic", label: "基础配置" }, { id: "basic", label: "基础配置" },
...@@ -163,6 +177,7 @@ export function SettingsPanels({ ...@@ -163,6 +177,7 @@ export function SettingsPanels({
const [revealedSecrets, setRevealedSecrets] = useState<SecretRevealState>({}) const [revealedSecrets, setRevealedSecrets] = useState<SecretRevealState>({})
const [revealedSecretValues, setRevealedSecretValues] = useState<SecretValueState>({}) const [revealedSecretValues, setRevealedSecretValues] = useState<SecretValueState>({})
const [copiedConfigValue, setCopiedConfigValue] = useState<string | null>(null) const [copiedConfigValue, setCopiedConfigValue] = useState<string | null>(null)
const [editingSettingsSections, setEditingSettingsSections] = useState<EditingSettingsSections>({})
const handleConfigSourceChange = (source: SettingsConfigSource) => { const handleConfigSourceChange = (source: SettingsConfigSource) => {
setConfigSource(source) setConfigSource(source)
...@@ -193,6 +208,46 @@ export function SettingsPanels({ ...@@ -193,6 +208,46 @@ export function SettingsPanels({
</div> </div>
) )
const renderEditableActions = ({
sectionId,
hasPending,
onReset,
onSave,
className
}: SectionActions & { sectionId: EditableSettingsSectionId }) => {
const editing = Boolean(editingSettingsSections[sectionId])
return (
<div className={["settings-actions-row", className].filter(Boolean).join(" ")}>
<button
type="button"
className="settings-action-button settings-action-button-secondary"
disabled={saving}
onClick={() => {
void onReset()
setEditingSettingsSections((current) => ({ ...current, [sectionId]: !editing }))
}}
>
{editing ? "取消" : "编辑"}
</button>
<button
type="button"
className="settings-action-button settings-action-button-primary"
disabled={saving || !editing || !hasPending}
onClick={() => {
void Promise.resolve(onSave()).then((saved) => {
if (saved !== false) {
setEditingSettingsSections((current) => ({ ...current, [sectionId]: false }))
}
})
}}
>
{saving ? "保存中" : "保存"}
</button>
</div>
)
}
const handleSecretRevealToggle = async (secretId: ConfigSecretId, value: string, setValue: SetDraft) => { const handleSecretRevealToggle = async (secretId: ConfigSecretId, value: string, setValue: SetDraft) => {
if (revealedSecrets[secretId]) { if (revealedSecrets[secretId]) {
setRevealedSecrets((current) => ({ ...current, [secretId]: false })) setRevealedSecrets((current) => ({ ...current, [secretId]: false }))
...@@ -220,7 +275,8 @@ export function SettingsPanels({ ...@@ -220,7 +275,8 @@ export function SettingsPanels({
setValue, setValue,
placeholder, placeholder,
inputClassName, inputClassName,
labelClassName labelClassName,
editable = true
}: { }: {
secretId: ConfigSecretId secretId: ConfigSecretId
configured: boolean configured: boolean
...@@ -230,6 +286,7 @@ export function SettingsPanels({ ...@@ -230,6 +286,7 @@ export function SettingsPanels({
placeholder: string placeholder: string
inputClassName?: string inputClassName?: string
labelClassName?: string labelClassName?: string
editable?: boolean
}) => { }) => {
const visible = configured && Boolean(revealedSecrets[secretId]) const visible = configured && Boolean(revealedSecrets[secretId])
const secretPlaceholder = configured && !value && !visible ? "••••••••••••" : placeholder const secretPlaceholder = configured && !value && !visible ? "••••••••••••" : placeholder
...@@ -243,7 +300,12 @@ export function SettingsPanels({ ...@@ -243,7 +300,12 @@ export function SettingsPanels({
value={value} value={value}
title={value || undefined} title={value || undefined}
placeholder={secretPlaceholder} placeholder={secretPlaceholder}
onChange={(event) => setValue(event.target.value)} readOnly={!editable}
onChange={(event) => {
if (editable) {
setValue(event.target.value)
}
}}
/> />
{configured ? ( {configured ? (
<button <button
...@@ -309,6 +371,31 @@ export function SettingsPanels({ ...@@ -309,6 +371,31 @@ export function SettingsPanels({
) )
} }
const renderEditableConfigValue = (label: string, value: string, setValue: SetDraft, fallbackValue: string | undefined, editing: boolean, placeholder = "") => {
if (!editing) {
return renderReadonlyConfigValue(label, value || fallbackValue)
}
return (
<label className="settings-input-label settings-editable-config-field">
<span className="settings-input-label-text">{label}</span>
<input
className="settings-truncated-input"
type="text"
value={value}
title={value || undefined}
placeholder={placeholder}
onChange={(event) => setValue(event.target.value)}
/>
</label>
)
}
const editingCopywritingConfig = Boolean(editingSettingsSections.copywriting)
const editingImageConfig = Boolean(editingSettingsSections.image)
const editingVideoConfig = Boolean(editingSettingsSections.video)
const editingDouyinRuntimeConfig = Boolean(editingSettingsSections.douyinRuntime)
return ( return (
<div className="settings-tabs-layout"> <div className="settings-tabs-layout">
<div className="settings-tabs-toolbar"> <div className="settings-tabs-toolbar">
...@@ -467,22 +554,24 @@ export function SettingsPanels({ ...@@ -467,22 +554,24 @@ export function SettingsPanels({
</div> </div>
<div className="model-config-card-body"> <div className="model-config-card-body">
<div className="settings-field-grid"> <div className="settings-field-grid">
{renderReadonlyConfigValue("base_url", config?.expertModelConfig.copywriting.baseUrl)} {renderEditableConfigValue("base_url", drafts.copywritingModelBaseUrl, setters.setCopywritingModelBaseUrl, config?.expertModelConfig.copywriting.baseUrl, editingCopywritingConfig, "请输入文案模型 base_url")}
{renderReadonlyConfigValue("model_id", config?.expertModelConfig.copywriting.modelId)} {renderEditableConfigValue("model_id", drafts.copywritingModelModelId, setters.setCopywritingModelModelId, config?.expertModelConfig.copywriting.modelId, editingCopywritingConfig, "请输入文案模型 model_id")}
{renderSecretInput({ {renderSecretInput({
secretId: "copywritingModelApiKey", secretId: "copywritingModelApiKey",
configured: Boolean(config?.expertModelConfig.copywriting.apiKeyConfigured), configured: Boolean(config?.expertModelConfig.copywriting.apiKeyConfigured),
label: "api_key", label: "api_key",
value: drafts.copywritingModelApiKey, value: drafts.copywritingModelApiKey,
setValue: setters.setCopywritingModelApiKey, setValue: setters.setCopywritingModelApiKey,
placeholder: config?.expertModelConfig.copywriting.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入文案模型 API Key" placeholder: config?.expertModelConfig.copywriting.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入文案模型 API Key",
editable: editingCopywritingConfig
})} })}
</div> </div>
</div> </div>
{renderActions({ {renderEditableActions({
sectionId: "copywriting",
hasPending: hasPendingCopywritingConfig, hasPending: hasPendingCopywritingConfig,
onReset: () => void onResetCopywritingConfig(), onReset: onResetCopywritingConfig,
onSave: () => void onSaveCopywritingConfig() onSave: onSaveCopywritingConfig
})} })}
</article> </article>
</div> </div>
...@@ -502,22 +591,24 @@ export function SettingsPanels({ ...@@ -502,22 +591,24 @@ export function SettingsPanels({
</div> </div>
<div className="model-config-card-body"> <div className="model-config-card-body">
<div className="settings-field-grid"> <div className="settings-field-grid">
{renderReadonlyConfigValue("base_url", config?.expertModelConfig.image.baseUrl)} {renderEditableConfigValue("base_url", drafts.imageModelBaseUrl, setters.setImageModelBaseUrl, config?.expertModelConfig.image.baseUrl, editingImageConfig, "请输入生图模型 base_url")}
{renderReadonlyConfigValue("model_id", config?.expertModelConfig.image.modelId)} {renderEditableConfigValue("model_id", drafts.imageModelModelId, setters.setImageModelModelId, config?.expertModelConfig.image.modelId, editingImageConfig, "请输入生图模型 model_id")}
{renderSecretInput({ {renderSecretInput({
secretId: "imageModelApiKey", secretId: "imageModelApiKey",
configured: Boolean(config?.expertModelConfig.image.apiKeyConfigured), configured: Boolean(config?.expertModelConfig.image.apiKeyConfigured),
label: "api_key", label: "api_key",
value: drafts.imageModelApiKey, value: drafts.imageModelApiKey,
setValue: setters.setImageModelApiKey, setValue: setters.setImageModelApiKey,
placeholder: config?.expertModelConfig.image.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入生图模型 API Key" placeholder: config?.expertModelConfig.image.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入生图模型 API Key",
editable: editingImageConfig
})} })}
</div> </div>
</div> </div>
{renderActions({ {renderEditableActions({
sectionId: "image",
hasPending: hasPendingImageConfig, hasPending: hasPendingImageConfig,
onReset: () => void onResetImageConfig(), onReset: onResetImageConfig,
onSave: () => void onSaveImageConfig() onSave: onSaveImageConfig
})} })}
</article> </article>
</div> </div>
...@@ -537,22 +628,24 @@ export function SettingsPanels({ ...@@ -537,22 +628,24 @@ export function SettingsPanels({
</div> </div>
<div className="model-config-card-body"> <div className="model-config-card-body">
<div className="settings-field-grid"> <div className="settings-field-grid">
{renderReadonlyConfigValue("base_url", config?.expertModelConfig.video.baseUrl)} {renderEditableConfigValue("base_url", drafts.videoModelBaseUrl, setters.setVideoModelBaseUrl, config?.expertModelConfig.video.baseUrl, editingVideoConfig, "请输入视频模型 base_url")}
{renderReadonlyConfigValue("model_id", config?.expertModelConfig.video.modelId)} {renderEditableConfigValue("model_id", drafts.videoModelModelId, setters.setVideoModelModelId, config?.expertModelConfig.video.modelId, editingVideoConfig, "请输入视频模型 model_id")}
{renderSecretInput({ {renderSecretInput({
secretId: "videoModelApiKey", secretId: "videoModelApiKey",
configured: Boolean(config?.expertModelConfig.video.apiKeyConfigured), configured: Boolean(config?.expertModelConfig.video.apiKeyConfigured),
label: "api_key", label: "api_key",
value: drafts.videoModelApiKey, value: drafts.videoModelApiKey,
setValue: setters.setVideoModelApiKey, setValue: setters.setVideoModelApiKey,
placeholder: config?.expertModelConfig.video.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入视频模型 API Key" placeholder: config?.expertModelConfig.video.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入视频模型 API Key",
editable: editingVideoConfig
})} })}
</div> </div>
</div> </div>
{renderActions({ {renderEditableActions({
sectionId: "video",
hasPending: hasPendingVideoConfig, hasPending: hasPendingVideoConfig,
onReset: () => void onResetVideoConfig(), onReset: onResetVideoConfig,
onSave: () => void onSaveVideoConfig() onSave: onSaveVideoConfig
})} })}
</article> </article>
</div> </div>
...@@ -629,15 +722,16 @@ export function SettingsPanels({ ...@@ -629,15 +722,16 @@ export function SettingsPanels({
</div> </div>
<div className="model-config-card-body"> <div className="model-config-card-body">
<div className="settings-field-grid"> <div className="settings-field-grid">
{renderReadonlyConfigValue("base_url", config?.douyinRuntimeConfig.videoAnalyzer.baseUrl)} {renderEditableConfigValue("base_url", drafts.videoAnalyzerBaseUrl, setters.setVideoAnalyzerBaseUrl, config?.douyinRuntimeConfig.videoAnalyzer.baseUrl, editingDouyinRuntimeConfig, "请输入 Video Analyzer base_url")}
{renderReadonlyConfigValue("model_id", config?.douyinRuntimeConfig.videoAnalyzer.modelId || "doubao-vision")} {renderEditableConfigValue("model_id", drafts.videoAnalyzerModelId, setters.setVideoAnalyzerModelId, config?.douyinRuntimeConfig.videoAnalyzer.modelId || "doubao-vision", editingDouyinRuntimeConfig, "请输入 Video Analyzer model_id")}
{renderSecretInput({ {renderSecretInput({
secretId: "videoAnalyzerApiKey", secretId: "videoAnalyzerApiKey",
configured: Boolean(config?.douyinRuntimeConfig.videoAnalyzer.apiKeyConfigured), configured: Boolean(config?.douyinRuntimeConfig.videoAnalyzer.apiKeyConfigured),
label: "api_key", label: "api_key",
value: drafts.videoAnalyzerApiKey, value: drafts.videoAnalyzerApiKey,
setValue: setters.setVideoAnalyzerApiKey, setValue: setters.setVideoAnalyzerApiKey,
placeholder: config?.douyinRuntimeConfig.videoAnalyzer.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入 Video Analyzer API Key" placeholder: config?.douyinRuntimeConfig.videoAnalyzer.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入 Video Analyzer API Key",
editable: editingDouyinRuntimeConfig
})} })}
</div> </div>
</div> </div>
...@@ -650,15 +744,16 @@ export function SettingsPanels({ ...@@ -650,15 +744,16 @@ export function SettingsPanels({
</div> </div>
<div className="model-config-card-body"> <div className="model-config-card-body">
<div className="settings-field-grid"> <div className="settings-field-grid">
{renderReadonlyConfigValue("base_url", config?.douyinRuntimeConfig.replicationBrief.baseUrl)} {renderEditableConfigValue("base_url", drafts.replicationBriefBaseUrl, setters.setReplicationBriefBaseUrl, config?.douyinRuntimeConfig.replicationBrief.baseUrl, editingDouyinRuntimeConfig, "请输入 Replication Brief base_url")}
{renderReadonlyConfigValue("model_id", config?.douyinRuntimeConfig.replicationBrief.modelId || "qwen-max")} {renderEditableConfigValue("model_id", drafts.replicationBriefModelId, setters.setReplicationBriefModelId, config?.douyinRuntimeConfig.replicationBrief.modelId || "qwen-max", editingDouyinRuntimeConfig, "请输入 Replication Brief model_id")}
{renderSecretInput({ {renderSecretInput({
secretId: "replicationBriefApiKey", secretId: "replicationBriefApiKey",
configured: Boolean(config?.douyinRuntimeConfig.replicationBrief.apiKeyConfigured), configured: Boolean(config?.douyinRuntimeConfig.replicationBrief.apiKeyConfigured),
label: "api_key", label: "api_key",
value: drafts.replicationBriefApiKey, value: drafts.replicationBriefApiKey,
setValue: setters.setReplicationBriefApiKey, setValue: setters.setReplicationBriefApiKey,
placeholder: config?.douyinRuntimeConfig.replicationBrief.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入 Replication Brief API Key" placeholder: config?.douyinRuntimeConfig.replicationBrief.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入 Replication Brief API Key",
editable: editingDouyinRuntimeConfig
})} })}
</div> </div>
</div> </div>
...@@ -671,24 +766,26 @@ export function SettingsPanels({ ...@@ -671,24 +766,26 @@ export function SettingsPanels({
</div> </div>
<div className="model-config-card-body"> <div className="model-config-card-body">
<div className="settings-field-grid"> <div className="settings-field-grid">
{renderReadonlyConfigValue("base_url", config?.douyinRuntimeConfig.vectcut.baseUrl)} {renderEditableConfigValue("base_url", drafts.vectcutBaseUrl, setters.setVectcutBaseUrl, config?.douyinRuntimeConfig.vectcut.baseUrl, editingDouyinRuntimeConfig, "请输入 VectCut base_url")}
{renderReadonlyConfigValue("file_base_url", config?.douyinRuntimeConfig.vectcut.fileBaseUrl)} {renderEditableConfigValue("file_base_url", drafts.vectcutFileBaseUrl, setters.setVectcutFileBaseUrl, config?.douyinRuntimeConfig.vectcut.fileBaseUrl, editingDouyinRuntimeConfig, "请输入 VectCut file_base_url")}
{renderSecretInput({ {renderSecretInput({
secretId: "vectcutApiKey", secretId: "vectcutApiKey",
configured: Boolean(config?.douyinRuntimeConfig.vectcut.apiKeyConfigured), configured: Boolean(config?.douyinRuntimeConfig.vectcut.apiKeyConfigured),
label: "api_key", label: "api_key",
value: drafts.vectcutApiKey, value: drafts.vectcutApiKey,
setValue: setters.setVectcutApiKey, setValue: setters.setVectcutApiKey,
placeholder: config?.douyinRuntimeConfig.vectcut.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入 VectCut API Key" placeholder: config?.douyinRuntimeConfig.vectcut.apiKeyConfigured ? "留空则保持当前已保存密钥" : "请输入 VectCut API Key",
editable: editingDouyinRuntimeConfig
})} })}
</div> </div>
</div> </div>
</article> </article>
<div className="settings-runtime-actions-panel"> <div className="settings-runtime-actions-panel">
{renderActions({ {renderEditableActions({
sectionId: "douyinRuntime",
hasPending: hasPendingDouyinRuntimeConfig, hasPending: hasPendingDouyinRuntimeConfig,
onReset: () => void onResetDouyinRuntimeConfig(), onReset: onResetDouyinRuntimeConfig,
onSave: () => void onSaveDouyinRuntimeConfig(), onSave: onSaveDouyinRuntimeConfig,
className: "settings-runtime-actions" className: "settings-runtime-actions"
})} })}
</div> </div>
......
...@@ -22,6 +22,24 @@ export interface DigitalHumanResetSettingsDrafts { ...@@ -22,6 +22,24 @@ export interface DigitalHumanResetSettingsDrafts {
digitalHumanQiniuSecretKey: string digitalHumanQiniuSecretKey: string
} }
export interface CopywritingResetSettingsDrafts {
copywritingModelBaseUrl: string
copywritingModelModelId: string
copywritingModelApiKey: string
}
export interface ImageResetSettingsDrafts {
imageModelBaseUrl: string
imageModelModelId: string
imageModelApiKey: string
}
export interface VideoResetSettingsDrafts {
videoModelBaseUrl: string
videoModelModelId: string
videoModelApiKey: string
}
export interface DouyinRuntimeResetSettingsDrafts { export interface DouyinRuntimeResetSettingsDrafts {
videoAnalyzerBaseUrl: string videoAnalyzerBaseUrl: string
videoAnalyzerModelId: string videoAnalyzerModelId: string
...@@ -58,20 +76,26 @@ export function getResetBasicSettingsDrafts(config: AppConfig): BasicResetSettin ...@@ -58,20 +76,26 @@ export function getResetBasicSettingsDrafts(config: AppConfig): BasicResetSettin
} }
} }
export function getResetCopywritingSettingsDrafts() { export function getResetCopywritingSettingsDrafts(config: AppConfig): CopywritingResetSettingsDrafts {
return { return {
copywritingModelBaseUrl: config.expertModelConfig.copywriting.baseUrl,
copywritingModelModelId: config.expertModelConfig.copywriting.modelId ?? "",
copywritingModelApiKey: "" copywritingModelApiKey: ""
} }
} }
export function getResetImageSettingsDrafts() { export function getResetImageSettingsDrafts(config: AppConfig): ImageResetSettingsDrafts {
return { return {
imageModelBaseUrl: config.expertModelConfig.image.baseUrl,
imageModelModelId: config.expertModelConfig.image.modelId ?? "",
imageModelApiKey: "" imageModelApiKey: ""
} }
} }
export function getResetVideoSettingsDrafts() { export function getResetVideoSettingsDrafts(config: AppConfig): VideoResetSettingsDrafts {
return { return {
videoModelBaseUrl: config.expertModelConfig.video.baseUrl,
videoModelModelId: config.expertModelConfig.video.modelId ?? "",
videoModelApiKey: "" videoModelApiKey: ""
} }
} }
......
...@@ -9,8 +9,14 @@ interface UseSaveSettingsOptions { ...@@ -9,8 +9,14 @@ interface UseSaveSettingsOptions {
lobsterKey: string lobsterKey: string
workspacePath: string workspacePath: string
imageModelApiKey: string imageModelApiKey: string
imageModelBaseUrl: string
imageModelModelId: string
videoModelApiKey: string videoModelApiKey: string
videoModelBaseUrl: string
videoModelModelId: string
copywritingModelApiKey: string copywritingModelApiKey: string
copywritingModelBaseUrl: string
copywritingModelModelId: string
digitalHumanVolcAccessKey: string digitalHumanVolcAccessKey: string
digitalHumanVolcSecretKey: string digitalHumanVolcSecretKey: string
digitalHumanQiniuAccessKey: string digitalHumanQiniuAccessKey: string
...@@ -37,14 +43,26 @@ interface UseSaveSettingsOptions { ...@@ -37,14 +43,26 @@ interface UseSaveSettingsOptions {
setWorkspacePathDraft(value: string): void setWorkspacePathDraft(value: string): void
setLobsterKeyDraft(value: string): void setLobsterKeyDraft(value: string): void
setImageModelApiKeyDraft(value: string): void setImageModelApiKeyDraft(value: string): void
setImageModelBaseUrlDraft(value: string): void
setImageModelModelIdDraft(value: string): void
setVideoModelApiKeyDraft(value: string): void setVideoModelApiKeyDraft(value: string): void
setVideoModelBaseUrlDraft(value: string): void
setVideoModelModelIdDraft(value: string): void
setCopywritingModelApiKeyDraft(value: string): void setCopywritingModelApiKeyDraft(value: string): void
setCopywritingModelBaseUrlDraft(value: string): void
setCopywritingModelModelIdDraft(value: string): void
setDigitalHumanVolcAccessKeyDraft(value: string): void setDigitalHumanVolcAccessKeyDraft(value: string): void
setDigitalHumanVolcSecretKeyDraft(value: string): void setDigitalHumanVolcSecretKeyDraft(value: string): void
setDigitalHumanQiniuAccessKeyDraft(value: string): void setDigitalHumanQiniuAccessKeyDraft(value: string): void
setDigitalHumanQiniuSecretKeyDraft(value: string): void setDigitalHumanQiniuSecretKeyDraft(value: string): void
setVideoAnalyzerBaseUrlDraft(value: string): void
setVideoAnalyzerModelIdDraft(value: string): void
setVideoAnalyzerApiKeyDraft(value: string): void setVideoAnalyzerApiKeyDraft(value: string): void
setReplicationBriefBaseUrlDraft(value: string): void
setReplicationBriefModelIdDraft(value: string): void
setReplicationBriefApiKeyDraft(value: string): void setReplicationBriefApiKeyDraft(value: string): void
setVectcutBaseUrlDraft(value: string): void
setVectcutFileBaseUrlDraft(value: string): void
setVectcutApiKeyDraft(value: string): void setVectcutApiKeyDraft(value: string): void
setXhsFeishuAppIdDraft(value: string): void setXhsFeishuAppIdDraft(value: string): void
setXhsFeishuAppSecretDraft(value: string): void setXhsFeishuAppSecretDraft(value: string): void
...@@ -80,7 +98,7 @@ export function useSaveSettings({ ...@@ -80,7 +98,7 @@ export function useSaveSettings({
resetDrafts?: (savedConfig: AppConfig) => void resetDrafts?: (savedConfig: AppConfig) => void
}) => { }) => {
if (!config || saving) { if (!config || saving) {
return return false
} }
const input: SaveConfigInput = { const input: SaveConfigInput = {
...@@ -119,15 +137,17 @@ export function useSaveSettings({ ...@@ -119,15 +137,17 @@ export function useSaveSettings({
options?.resetDrafts?.(savedConfig) options?.resetDrafts?.(savedConfig)
setters.setInfoText(options?.successMessage ?? (trimmedLobsterKey ? labels.saveSuccessPending : labels.saveSuccessApplied)) setters.setInfoText(options?.successMessage ?? (trimmedLobsterKey ? labels.saveSuccessPending : labels.saveSuccessApplied))
void refresh(false) void refresh(false)
return true
} catch (error) { } catch (error) {
setters.setErrorText(normalizeError(error)) setters.setErrorText(normalizeError(error))
return false
} finally { } finally {
setters.setSaving(false) setters.setSaving(false)
} }
}, [config, desktopApi, labels.saveSuccessApplied, labels.saveSuccessPending, normalizeError, refresh, saving, setters]) }, [config, desktopApi, labels.saveSuccessApplied, labels.saveSuccessPending, normalizeError, refresh, saving, setters])
const saveLobsterKey = useCallback(async () => { const saveLobsterKey = useCallback(async () => {
await saveConfig({ return await saveConfig({
lobsterKey: drafts.lobsterKey, lobsterKey: drafts.lobsterKey,
resetDrafts: () => { resetDrafts: () => {
setters.setLobsterKeyDraft("") setters.setLobsterKeyDraft("")
...@@ -165,7 +185,7 @@ export function useSaveSettings({ ...@@ -165,7 +185,7 @@ export function useSaveSettings({
}, [config, setters]) }, [config, setters])
const saveWorkspaceDirectory = useCallback(async () => { const saveWorkspaceDirectory = useCallback(async () => {
await saveConfig({ return await saveConfig({
workspacePath: drafts.workspacePath, workspacePath: drafts.workspacePath,
successMessage: labels.workspaceSaved, successMessage: labels.workspaceSaved,
resetDrafts: (savedConfig) => { resetDrafts: (savedConfig) => {
...@@ -175,7 +195,7 @@ export function useSaveSettings({ ...@@ -175,7 +195,7 @@ export function useSaveSettings({
}, [drafts.workspacePath, labels.workspaceSaved, saveConfig, setters]) }, [drafts.workspacePath, labels.workspaceSaved, saveConfig, setters])
const saveXhsFeishuConfig = useCallback(async () => { const saveXhsFeishuConfig = useCallback(async () => {
await saveConfig({ return await saveConfig({
xhsFeishuConfig: { xhsFeishuConfig: {
appId: drafts.xhsFeishuAppId.trim() || undefined, appId: drafts.xhsFeishuAppId.trim() || undefined,
appSecret: drafts.xhsFeishuAppSecret.trim() || undefined, appSecret: drafts.xhsFeishuAppSecret.trim() || undefined,
...@@ -192,46 +212,58 @@ export function useSaveSettings({ ...@@ -192,46 +212,58 @@ export function useSaveSettings({
}, [drafts.xhsFeishuAppId, drafts.xhsFeishuAppSecret, drafts.xhsFeishuAppToken, drafts.xhsFeishuTableId, saveConfig, setters]) }, [drafts.xhsFeishuAppId, drafts.xhsFeishuAppSecret, drafts.xhsFeishuAppToken, drafts.xhsFeishuTableId, saveConfig, setters])
const saveCopywritingConfig = useCallback(async () => { const saveCopywritingConfig = useCallback(async () => {
await saveConfig({ return await saveConfig({
expertModelConfig: { expertModelConfig: {
copywriting: { copywriting: {
baseUrl: drafts.copywritingModelBaseUrl.trim() || undefined,
modelId: drafts.copywritingModelModelId.trim() || undefined,
apiKey: drafts.copywritingModelApiKey.trim() || undefined apiKey: drafts.copywritingModelApiKey.trim() || undefined
} }
}, },
resetDrafts: () => { resetDrafts: (savedConfig) => {
setters.setCopywritingModelApiKeyDraft("") setters.setCopywritingModelApiKeyDraft("")
setters.setCopywritingModelBaseUrlDraft(savedConfig.expertModelConfig.copywriting.baseUrl)
setters.setCopywritingModelModelIdDraft(savedConfig.expertModelConfig.copywriting.modelId ?? "")
} }
}) })
}, [drafts.copywritingModelApiKey, saveConfig, setters]) }, [drafts.copywritingModelApiKey, drafts.copywritingModelBaseUrl, drafts.copywritingModelModelId, saveConfig, setters])
const saveImageConfig = useCallback(async () => { const saveImageConfig = useCallback(async () => {
await saveConfig({ return await saveConfig({
expertModelConfig: { expertModelConfig: {
image: { image: {
baseUrl: drafts.imageModelBaseUrl.trim() || undefined,
modelId: drafts.imageModelModelId.trim() || undefined,
apiKey: drafts.imageModelApiKey.trim() || undefined apiKey: drafts.imageModelApiKey.trim() || undefined
} }
}, },
resetDrafts: () => { resetDrafts: (savedConfig) => {
setters.setImageModelApiKeyDraft("") setters.setImageModelApiKeyDraft("")
setters.setImageModelBaseUrlDraft(savedConfig.expertModelConfig.image.baseUrl)
setters.setImageModelModelIdDraft(savedConfig.expertModelConfig.image.modelId ?? "")
} }
}) })
}, [drafts.imageModelApiKey, saveConfig, setters]) }, [drafts.imageModelApiKey, drafts.imageModelBaseUrl, drafts.imageModelModelId, saveConfig, setters])
const saveVideoConfig = useCallback(async () => { const saveVideoConfig = useCallback(async () => {
await saveConfig({ return await saveConfig({
expertModelConfig: { expertModelConfig: {
video: { video: {
baseUrl: drafts.videoModelBaseUrl.trim() || undefined,
modelId: drafts.videoModelModelId.trim() || undefined,
apiKey: drafts.videoModelApiKey.trim() || undefined apiKey: drafts.videoModelApiKey.trim() || undefined
} }
}, },
resetDrafts: () => { resetDrafts: (savedConfig) => {
setters.setVideoModelApiKeyDraft("") setters.setVideoModelApiKeyDraft("")
setters.setVideoModelBaseUrlDraft(savedConfig.expertModelConfig.video.baseUrl)
setters.setVideoModelModelIdDraft(savedConfig.expertModelConfig.video.modelId ?? "")
} }
}) })
}, [drafts.videoModelApiKey, saveConfig, setters]) }, [drafts.videoModelApiKey, drafts.videoModelBaseUrl, drafts.videoModelModelId, saveConfig, setters])
const saveDigitalHumanConfig = useCallback(async () => { const saveDigitalHumanConfig = useCallback(async () => {
await saveConfig({ return await saveConfig({
expertModelConfig: { expertModelConfig: {
digitalHuman: { digitalHuman: {
volcAccessKey: drafts.digitalHumanVolcAccessKey.trim() || undefined, volcAccessKey: drafts.digitalHumanVolcAccessKey.trim() || undefined,
...@@ -250,7 +282,7 @@ export function useSaveSettings({ ...@@ -250,7 +282,7 @@ export function useSaveSettings({
}, [drafts.digitalHumanQiniuAccessKey, drafts.digitalHumanQiniuSecretKey, drafts.digitalHumanVolcAccessKey, drafts.digitalHumanVolcSecretKey, saveConfig, setters]) }, [drafts.digitalHumanQiniuAccessKey, drafts.digitalHumanQiniuSecretKey, drafts.digitalHumanVolcAccessKey, drafts.digitalHumanVolcSecretKey, saveConfig, setters])
const saveDouyinRuntimeConfig = useCallback(async () => { const saveDouyinRuntimeConfig = useCallback(async () => {
await saveConfig({ return await saveConfig({
douyinRuntimeConfig: { douyinRuntimeConfig: {
videoAnalyzer: { videoAnalyzer: {
baseUrl: drafts.videoAnalyzerBaseUrl.trim() || undefined, baseUrl: drafts.videoAnalyzerBaseUrl.trim() || undefined,
...@@ -268,9 +300,15 @@ export function useSaveSettings({ ...@@ -268,9 +300,15 @@ export function useSaveSettings({
apiKey: drafts.vectcutApiKey.trim() || undefined apiKey: drafts.vectcutApiKey.trim() || undefined
} }
}, },
resetDrafts: () => { resetDrafts: (savedConfig) => {
setters.setVideoAnalyzerBaseUrlDraft(savedConfig.douyinRuntimeConfig.videoAnalyzer.baseUrl)
setters.setVideoAnalyzerModelIdDraft(savedConfig.douyinRuntimeConfig.videoAnalyzer.modelId ?? "")
setters.setVideoAnalyzerApiKeyDraft("") setters.setVideoAnalyzerApiKeyDraft("")
setters.setReplicationBriefBaseUrlDraft(savedConfig.douyinRuntimeConfig.replicationBrief.baseUrl)
setters.setReplicationBriefModelIdDraft(savedConfig.douyinRuntimeConfig.replicationBrief.modelId ?? "")
setters.setReplicationBriefApiKeyDraft("") setters.setReplicationBriefApiKeyDraft("")
setters.setVectcutBaseUrlDraft(savedConfig.douyinRuntimeConfig.vectcut.baseUrl)
setters.setVectcutFileBaseUrlDraft(savedConfig.douyinRuntimeConfig.vectcut.fileBaseUrl)
setters.setVectcutApiKeyDraft("") setters.setVectcutApiKeyDraft("")
} }
}) })
......
...@@ -15,8 +15,14 @@ export function useSettingsState(config: AppConfig | null) { ...@@ -15,8 +15,14 @@ export function useSettingsState(config: AppConfig | null) {
const [lobsterKeyDraft, setLobsterKeyDraft] = useState(""); const [lobsterKeyDraft, setLobsterKeyDraft] = useState("");
const [workspacePathDraft, setWorkspacePathDraft] = useState(""); const [workspacePathDraft, setWorkspacePathDraft] = useState("");
const [imageModelApiKeyDraft, setImageModelApiKeyDraft] = useState(""); const [imageModelApiKeyDraft, setImageModelApiKeyDraft] = useState("");
const [imageModelBaseUrlDraft, setImageModelBaseUrlDraft] = useState("");
const [imageModelModelIdDraft, setImageModelModelIdDraft] = useState("");
const [videoModelApiKeyDraft, setVideoModelApiKeyDraft] = useState(""); const [videoModelApiKeyDraft, setVideoModelApiKeyDraft] = useState("");
const [videoModelBaseUrlDraft, setVideoModelBaseUrlDraft] = useState("");
const [videoModelModelIdDraft, setVideoModelModelIdDraft] = useState("");
const [copywritingModelApiKeyDraft, setCopywritingModelApiKeyDraft] = useState(""); const [copywritingModelApiKeyDraft, setCopywritingModelApiKeyDraft] = useState("");
const [copywritingModelBaseUrlDraft, setCopywritingModelBaseUrlDraft] = useState("");
const [copywritingModelModelIdDraft, setCopywritingModelModelIdDraft] = useState("");
const [digitalHumanVolcAccessKeyDraft, setDigitalHumanVolcAccessKeyDraft] = useState(""); const [digitalHumanVolcAccessKeyDraft, setDigitalHumanVolcAccessKeyDraft] = useState("");
const [digitalHumanVolcSecretKeyDraft, setDigitalHumanVolcSecretKeyDraft] = useState(""); const [digitalHumanVolcSecretKeyDraft, setDigitalHumanVolcSecretKeyDraft] = useState("");
const [digitalHumanQiniuAccessKeyDraft, setDigitalHumanQiniuAccessKeyDraft] = useState(""); const [digitalHumanQiniuAccessKeyDraft, setDigitalHumanQiniuAccessKeyDraft] = useState("");
...@@ -45,8 +51,14 @@ export function useSettingsState(config: AppConfig | null) { ...@@ -45,8 +51,14 @@ export function useSettingsState(config: AppConfig | null) {
); );
const hasPendingModelKeys = Boolean( const hasPendingModelKeys = Boolean(
imageModelApiKeyDraft.trim() imageModelApiKeyDraft.trim()
|| imageModelBaseUrlDraft.trim() !== (config?.expertModelConfig.image.baseUrl ?? "").trim()
|| imageModelModelIdDraft.trim() !== (config?.expertModelConfig.image.modelId ?? "").trim()
|| videoModelApiKeyDraft.trim() || videoModelApiKeyDraft.trim()
|| videoModelBaseUrlDraft.trim() !== (config?.expertModelConfig.video.baseUrl ?? "").trim()
|| videoModelModelIdDraft.trim() !== (config?.expertModelConfig.video.modelId ?? "").trim()
|| copywritingModelApiKeyDraft.trim() || copywritingModelApiKeyDraft.trim()
|| copywritingModelBaseUrlDraft.trim() !== (config?.expertModelConfig.copywriting.baseUrl ?? "").trim()
|| copywritingModelModelIdDraft.trim() !== (config?.expertModelConfig.copywriting.modelId ?? "").trim()
|| digitalHumanVolcAccessKeyDraft.trim() || digitalHumanVolcAccessKeyDraft.trim()
|| digitalHumanVolcSecretKeyDraft.trim() || digitalHumanVolcSecretKeyDraft.trim()
|| digitalHumanQiniuAccessKeyDraft.trim() || digitalHumanQiniuAccessKeyDraft.trim()
...@@ -69,10 +81,22 @@ export function useSettingsState(config: AppConfig | null) { ...@@ -69,10 +81,22 @@ export function useSettingsState(config: AppConfig | null) {
setWorkspacePathDraft, setWorkspacePathDraft,
imageModelApiKeyDraft, imageModelApiKeyDraft,
setImageModelApiKeyDraft, setImageModelApiKeyDraft,
imageModelBaseUrlDraft,
setImageModelBaseUrlDraft,
imageModelModelIdDraft,
setImageModelModelIdDraft,
videoModelApiKeyDraft, videoModelApiKeyDraft,
setVideoModelApiKeyDraft, setVideoModelApiKeyDraft,
videoModelBaseUrlDraft,
setVideoModelBaseUrlDraft,
videoModelModelIdDraft,
setVideoModelModelIdDraft,
copywritingModelApiKeyDraft, copywritingModelApiKeyDraft,
setCopywritingModelApiKeyDraft, setCopywritingModelApiKeyDraft,
copywritingModelBaseUrlDraft,
setCopywritingModelBaseUrlDraft,
copywritingModelModelIdDraft,
setCopywritingModelModelIdDraft,
digitalHumanVolcAccessKeyDraft, digitalHumanVolcAccessKeyDraft,
setDigitalHumanVolcAccessKeyDraft, setDigitalHumanVolcAccessKeyDraft,
digitalHumanVolcSecretKeyDraft, digitalHumanVolcSecretKeyDraft,
......
...@@ -67,7 +67,7 @@ interface UseSmokeActionHandlersDeps { ...@@ -67,7 +67,7 @@ interface UseSmokeActionHandlersDeps {
douyinRuntimeConfig?: SaveConfigInput["douyinRuntimeConfig"] douyinRuntimeConfig?: SaveConfigInput["douyinRuntimeConfig"]
xhsFeishuConfig?: SaveConfigInput["xhsFeishuConfig"] xhsFeishuConfig?: SaveConfigInput["xhsFeishuConfig"]
successMessage?: string successMessage?: string
}) => Promise<void> }) => Promise<boolean>
waitForSmokeConfigPublish: (expected: AppConfig["expertModelConfig"]) => Promise<void> waitForSmokeConfigPublish: (expected: AppConfig["expertModelConfig"]) => Promise<void>
resolveHomeIntentSuggestion: (prompt?: string) => Promise<{ resolveHomeIntentSuggestion: (prompt?: string) => Promise<{
visible: boolean visible: boolean
......
...@@ -306,15 +306,18 @@ export const mockDesktopApi = { ...@@ -306,15 +306,18 @@ export const mockDesktopApi = {
runtimeMode: input.runtimeMode, runtimeMode: input.runtimeMode,
expertModelConfig: { expertModelConfig: {
image: { image: {
...FIXED_EXPERT_MODEL_ENDPOINTS.image, baseUrl: input.expertModelConfig?.image?.baseUrl?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.image.baseUrl,
modelId: input.expertModelConfig?.image?.modelId?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.image.modelId,
apiKeyConfigured: Boolean(input.expertModelConfig?.image?.apiKey?.trim()), apiKeyConfigured: Boolean(input.expertModelConfig?.image?.apiKey?.trim()),
}, },
video: { video: {
...FIXED_EXPERT_MODEL_ENDPOINTS.video, baseUrl: input.expertModelConfig?.video?.baseUrl?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.video.baseUrl,
modelId: input.expertModelConfig?.video?.modelId?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.video.modelId,
apiKeyConfigured: Boolean(input.expertModelConfig?.video?.apiKey?.trim()) apiKeyConfigured: Boolean(input.expertModelConfig?.video?.apiKey?.trim())
}, },
copywriting: { copywriting: {
...FIXED_EXPERT_MODEL_ENDPOINTS.copywriting, baseUrl: input.expertModelConfig?.copywriting?.baseUrl?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.copywriting.baseUrl,
modelId: input.expertModelConfig?.copywriting?.modelId?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.copywriting.modelId,
apiKeyConfigured: Boolean(input.expertModelConfig?.copywriting?.apiKey?.trim()), apiKeyConfigured: Boolean(input.expertModelConfig?.copywriting?.apiKey?.trim()),
}, },
digitalHuman: { digitalHuman: {
......
...@@ -87,14 +87,20 @@ test("basic reset only affects lobster key and workspace path", () => { ...@@ -87,14 +87,20 @@ test("basic reset only affects lobster key and workspace path", () => {
}) })
}) })
test("single model resets only clear the current module key", () => { test("single model resets restore saved endpoints and clear the current module key", () => {
assert.deepEqual(getResetCopywritingSettingsDrafts(), { assert.deepEqual(getResetCopywritingSettingsDrafts(config), {
copywritingModelBaseUrl: "https://copy.example.test",
copywritingModelModelId: "copy-model",
copywritingModelApiKey: "", copywritingModelApiKey: "",
}) })
assert.deepEqual(getResetImageSettingsDrafts(), { assert.deepEqual(getResetImageSettingsDrafts(config), {
imageModelBaseUrl: "https://image.example.test",
imageModelModelId: "image-model",
imageModelApiKey: "", imageModelApiKey: "",
}) })
assert.deepEqual(getResetVideoSettingsDrafts(), { assert.deepEqual(getResetVideoSettingsDrafts(config), {
videoModelBaseUrl: "https://video.example.test",
videoModelModelId: "video-model",
videoModelApiKey: "", videoModelApiKey: "",
}) })
}) })
......
...@@ -72,6 +72,8 @@ test("settings secret inputs default hidden and gate reveal buttons behind saved ...@@ -72,6 +72,8 @@ test("settings secret inputs default hidden and gate reveal buttons behind saved
assert.match(settingsPanelsSource, /const visible = configured && Boolean\(revealedSecrets\[secretId\]\)/) assert.match(settingsPanelsSource, /const visible = configured && Boolean\(revealedSecrets\[secretId\]\)/)
assert.match(settingsPanelsSource, /const secretPlaceholder = configured && !value && !visible \? "••••••••••••" : placeholder/) assert.match(settingsPanelsSource, /const secretPlaceholder = configured && !value && !visible \? "••••••••••••" : placeholder/)
assert.match(settingsPanelsSource, /placeholder=\{secretPlaceholder\}/) assert.match(settingsPanelsSource, /placeholder=\{secretPlaceholder\}/)
assert.match(settingsPanelsSource, /editable = true/)
assert.match(settingsPanelsSource, /readOnly=\{!editable\}/)
assert.match(settingsPanelsSource, /configured \? \(/) assert.match(settingsPanelsSource, /configured \? \(/)
assert.match(settingsPanelsSource, /secretId: "lobsterKey"/) assert.match(settingsPanelsSource, /secretId: "lobsterKey"/)
assert.match(settingsPanelsSource, /configured: workspaceApiKeyConfigured/) assert.match(settingsPanelsSource, /configured: workspaceApiKeyConfigured/)
...@@ -98,29 +100,37 @@ test("settings secret inputs default hidden and gate reveal buttons behind saved ...@@ -98,29 +100,37 @@ test("settings secret inputs default hidden and gate reveal buttons behind saved
assert.match(settingsStylesSource, /\.settings-secret-reveal-button svg\s*\{[\s\S]*?width:\s*22px;[\s\S]*?height:\s*22px;/m) assert.match(settingsStylesSource, /\.settings-secret-reveal-button svg\s*\{[\s\S]*?width:\s*22px;[\s\S]*?height:\s*22px;/m)
}) })
test("fixed expert model cards show base_url and model_id as read-only values", () => { test("expert model cards become editable after clicking edit", () => {
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.copywriting\.baseUrl/) assert.match(settingsPanelsSource, /const \[editingSettingsSections, setEditingSettingsSections\] = useState<EditingSettingsSections>\(\{\}\)/)
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.copywriting\.modelId/) assert.match(settingsPanelsSource, /const renderEditableActions = \(/)
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.image\.baseUrl/) assert.match(settingsPanelsSource, /\{editing \? "取消" : "编辑"\}/)
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.image\.modelId/) assert.match(settingsPanelsSource, /disabled=\{saving \|\| !editing \|\| !hasPending\}/)
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.video\.baseUrl/) assert.match(settingsPanelsSource, /Promise\.resolve\(onSave\(\)\)\.then\(\(saved\) =>/)
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.video\.modelId/) assert.match(settingsPanelsSource, /if \(saved !== false\)/)
assert.match(settingsPanelsSource, /sectionId: "copywriting"/)
assert.match(settingsPanelsSource, /sectionId: "image"/)
assert.match(settingsPanelsSource, /sectionId: "video"/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.copywritingModelBaseUrl, setters\.setCopywritingModelBaseUrl, config\?\.expertModelConfig\.copywriting\.baseUrl, editingCopywritingConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.copywritingModelModelId, setters\.setCopywritingModelModelId, config\?\.expertModelConfig\.copywriting\.modelId, editingCopywritingConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.imageModelBaseUrl, setters\.setImageModelBaseUrl, config\?\.expertModelConfig\.image\.baseUrl, editingImageConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.imageModelModelId, setters\.setImageModelModelId, config\?\.expertModelConfig\.image\.modelId, editingImageConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.videoModelBaseUrl, setters\.setVideoModelBaseUrl, config\?\.expertModelConfig\.video\.baseUrl, editingVideoConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.videoModelModelId, setters\.setVideoModelModelId, config\?\.expertModelConfig\.video\.modelId, editingVideoConfig/)
assert.match(settingsPanelsSource, /onSave: onSaveCopywritingConfig/)
assert.match(settingsPanelsSource, /onSave: onSaveImageConfig/)
assert.match(settingsPanelsSource, /onSave: onSaveVideoConfig/)
assert.match(settingsPanelsSource, /settings-readonly-config-value/) assert.match(settingsPanelsSource, /settings-readonly-config-value/)
}) })
test("douyin runtime base urls, model ids, and file base url use copyable read-only values", () => { test("douyin runtime base urls, model ids, and file base url enter editable mode", () => {
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("base_url", config\?\.douyinRuntimeConfig\.videoAnalyzer\.baseUrl\)/) assert.match(settingsPanelsSource, /sectionId: "douyinRuntime"/)
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("model_id", config\?\.douyinRuntimeConfig\.videoAnalyzer\.modelId \|\| "doubao-vision"\)/) assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.videoAnalyzerBaseUrl, setters\.setVideoAnalyzerBaseUrl, config\?\.douyinRuntimeConfig\.videoAnalyzer\.baseUrl, editingDouyinRuntimeConfig/)
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("base_url", config\?\.douyinRuntimeConfig\.replicationBrief\.baseUrl\)/) assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.videoAnalyzerModelId, setters\.setVideoAnalyzerModelId, config\?\.douyinRuntimeConfig\.videoAnalyzer\.modelId \|\| "doubao-vision", editingDouyinRuntimeConfig/)
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("model_id", config\?\.douyinRuntimeConfig\.replicationBrief\.modelId \|\| "qwen-max"\)/) assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.replicationBriefBaseUrl, setters\.setReplicationBriefBaseUrl, config\?\.douyinRuntimeConfig\.replicationBrief\.baseUrl, editingDouyinRuntimeConfig/)
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("base_url", config\?\.douyinRuntimeConfig\.vectcut\.baseUrl\)/) assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.replicationBriefModelId, setters\.setReplicationBriefModelId, config\?\.douyinRuntimeConfig\.replicationBrief\.modelId \|\| "qwen-max", editingDouyinRuntimeConfig/)
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("file_base_url", config\?\.douyinRuntimeConfig\.vectcut\.fileBaseUrl\)/) assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.vectcutBaseUrl, setters\.setVectcutBaseUrl, config\?\.douyinRuntimeConfig\.vectcut\.baseUrl, editingDouyinRuntimeConfig/)
assert.doesNotMatch(settingsPanelsSource, /setVideoAnalyzerBaseUrl\(event\.target\.value\)/) assert.match(settingsPanelsSource, /renderEditableConfigValue\("file_base_url", drafts\.vectcutFileBaseUrl, setters\.setVectcutFileBaseUrl, config\?\.douyinRuntimeConfig\.vectcut\.fileBaseUrl, editingDouyinRuntimeConfig/)
assert.doesNotMatch(settingsPanelsSource, /setVideoAnalyzerModelId\(event\.target\.value\)/) assert.match(settingsPanelsSource, /onSave: onSaveDouyinRuntimeConfig/)
assert.doesNotMatch(settingsPanelsSource, /setReplicationBriefBaseUrl\(event\.target\.value\)/)
assert.doesNotMatch(settingsPanelsSource, /setReplicationBriefModelId\(event\.target\.value\)/)
assert.doesNotMatch(settingsPanelsSource, /setVectcutBaseUrl\(event\.target\.value\)/)
assert.doesNotMatch(settingsPanelsSource, /setVectcutFileBaseUrl\(event\.target\.value\)/)
}) })
test("read-only config values expose full values by title and copy on click or keyboard", () => { test("read-only config values expose full values by title and copy on click or keyboard", () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment