Commit 9d7f1c0a authored by edy's avatar edy

feat(ui): port Windows settings panel to Mac, add Feishu Mobile config

- Add Feishu Mobile Config feature across all layers (types, secrets, config, IPC, UI)
- Replace Mac glassmorphism design with Windows flat design (settings.css)
- Improve secret reveal UX: loading spinner, error handling, touched state tracking
- Change basic tab layout from single compact card to 3-card bento layout
- Add Modal component (apps/ui/src/components/ui/Modal.tsx)
- Start editable sections in edit mode by default
- Add workspace directory save/restore buttons to SettingsPanels
- Add reveal error display in settings toolbar
- Clean up dead CSS classes from old basic tab layout
- Fix modal panel to use flat design matching settings style
Co-Authored-By: 's avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 703f338b
......@@ -609,7 +609,9 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
xhsFeishuAppId,
xhsFeishuAppSecret,
xhsFeishuAppToken,
xhsFeishuTableId
xhsFeishuTableId,
feishuMobileAppId,
feishuMobileAppSecret
] = await Promise.all([
secretManager.getApiKey(),
getEffectiveGatewayToken(config),
......@@ -627,7 +629,9 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
secretManager.getXhsFeishuAppId(),
secretManager.getXhsFeishuAppSecret(),
secretManager.getXhsFeishuAppToken(),
secretManager.getXhsFeishuTableId()
secretManager.getXhsFeishuTableId(),
secretManager.getFeishuMobileAppId(),
secretManager.getFeishuMobileAppSecret()
]);
const nextConfig: AppConfig = {
......@@ -676,6 +680,10 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
appSecretConfigured: Boolean(xhsFeishuAppSecret),
appTokenConfigured: Boolean(xhsFeishuAppToken),
tableIdConfigured: Boolean(xhsFeishuTableId)
},
feishuMobileConfig: {
appIdConfigured: Boolean(feishuMobileAppId),
appSecretConfigured: Boolean(feishuMobileAppSecret)
}
};
......@@ -695,7 +703,9 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
|| nextConfig.xhsFeishuConfig.appIdConfigured !== config.xhsFeishuConfig.appIdConfigured
|| nextConfig.xhsFeishuConfig.appSecretConfigured !== config.xhsFeishuConfig.appSecretConfigured
|| nextConfig.xhsFeishuConfig.appTokenConfigured !== config.xhsFeishuConfig.appTokenConfigured
|| nextConfig.xhsFeishuConfig.tableIdConfigured !== config.xhsFeishuConfig.tableIdConfigured;
|| nextConfig.xhsFeishuConfig.tableIdConfigured !== config.xhsFeishuConfig.tableIdConfigured
|| nextConfig.feishuMobileConfig.appIdConfigured !== config.feishuMobileConfig.appIdConfigured
|| nextConfig.feishuMobileConfig.appSecretConfigured !== config.feishuMobileConfig.appSecretConfigured;
if (secretStateChanged) {
await configService.persist({
......@@ -760,6 +770,12 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
case "xhsFeishuTableId":
value = await secretManager.getXhsFeishuTableId();
break;
case "feishuMobileAppId":
value = await secretManager.getFeishuMobileAppId();
break;
case "feishuMobileAppSecret":
value = await secretManager.getFeishuMobileAppSecret();
break;
default:
throw new Error("Unsupported config secret id.");
}
......
......@@ -13,7 +13,8 @@ import {
type RuntimeModePreference,
type SaveConfigInput,
type SetupMode,
type XhsFeishuConfig
type XhsFeishuConfig,
type FeishuMobileConfig
} from "@qjclaw/shared-types";
export type RuntimeCloudApiBaseUrlSource = "config" | "env" | "default";
......@@ -76,6 +77,7 @@ interface LegacyConfig {
};
};
xhsFeishuConfig?: Partial<XhsFeishuConfig>;
feishuMobileConfig?: Partial<FeishuMobileConfig>;
}
function normalizeGatewayUrl(raw: string): string {
......@@ -217,6 +219,13 @@ function createDefaultXhsFeishuConfig(): XhsFeishuConfig {
};
}
function createDefaultFeishuMobileConfig(): FeishuMobileConfig {
return {
appIdConfigured: false,
appSecretConfigured: false
};
}
function mergeExpertModelConfig(
current: ExpertModelConfig,
input?: SaveConfigInput["expertModelConfig"]
......@@ -300,6 +309,16 @@ function mergeXhsFeishuConfig(
};
}
function mergeFeishuMobileConfig(
current: FeishuMobileConfig,
input?: SaveConfigInput["feishuMobileConfig"]
): FeishuMobileConfig {
return {
appIdConfigured: typeof input?.appId === "string" ? Boolean(input.appId.trim()) : current.appIdConfigured,
appSecretConfigured: typeof input?.appSecret === "string" ? Boolean(input.appSecret.trim()) : current.appSecretConfigured
};
}
export function getRuntimeCloudApiTarget(config: Pick<AppConfig, "runtimeCloudApiBaseUrl">): RuntimeCloudApiTarget {
return resolveRuntimeCloudApiTarget(config.runtimeCloudApiBaseUrl);
}
......@@ -334,7 +353,8 @@ export class AppConfigService {
runtimeMode: normalizeRuntimeMode(input.runtimeMode),
expertModelConfig: mergeExpertModelConfig(current.expertModelConfig, input.expertModelConfig),
douyinRuntimeConfig: mergeDouyinRuntimeConfig(current.douyinRuntimeConfig, input.douyinRuntimeConfig),
xhsFeishuConfig: mergeXhsFeishuConfig(current.xhsFeishuConfig, input.xhsFeishuConfig)
xhsFeishuConfig: mergeXhsFeishuConfig(current.xhsFeishuConfig, input.xhsFeishuConfig),
feishuMobileConfig: mergeFeishuMobileConfig(current.feishuMobileConfig, input.feishuMobileConfig)
};
await this.writeConfig(config);
......@@ -372,7 +392,8 @@ export class AppConfigService {
runtimeMode: normalizeRuntimeMode(process.env.QJCLAW_RUNTIME_MODE),
expertModelConfig: createDefaultExpertModelConfig(),
douyinRuntimeConfig: createDefaultDouyinRuntimeConfig(),
xhsFeishuConfig: createDefaultXhsFeishuConfig()
xhsFeishuConfig: createDefaultXhsFeishuConfig(),
feishuMobileConfig: createDefaultFeishuMobileConfig()
};
}
......@@ -380,6 +401,7 @@ export class AppConfigService {
const defaultExpertModelConfig = createDefaultExpertModelConfig();
const defaultDouyinRuntimeConfig = createDefaultDouyinRuntimeConfig();
const defaultXhsFeishuConfig = createDefaultXhsFeishuConfig();
const defaultFeishuMobileConfig = createDefaultFeishuMobileConfig();
return {
setupMode: normalizeSetupMode(config.setupMode),
provider: config.provider ?? "openai",
......@@ -453,6 +475,10 @@ export class AppConfigService {
appSecretConfigured: Boolean(config.xhsFeishuConfig?.appSecretConfigured ?? defaultXhsFeishuConfig.appSecretConfigured),
appTokenConfigured: Boolean(config.xhsFeishuConfig?.appTokenConfigured ?? defaultXhsFeishuConfig.appTokenConfigured),
tableIdConfigured: Boolean(config.xhsFeishuConfig?.tableIdConfigured ?? defaultXhsFeishuConfig.tableIdConfigured)
},
feishuMobileConfig: {
appIdConfigured: Boolean(config.feishuMobileConfig?.appIdConfigured ?? defaultFeishuMobileConfig.appIdConfigured),
appSecretConfigured: Boolean(config.feishuMobileConfig?.appSecretConfigured ?? defaultFeishuMobileConfig.appSecretConfigured)
}
};
}
......
......@@ -21,6 +21,8 @@ interface SecretRecord {
xhsFeishuAppSecret?: string;
xhsFeishuAppToken?: string;
xhsFeishuTableId?: string;
feishuMobileAppId?: string;
feishuMobileAppSecret?: string;
}
interface SecretAccessor {
......@@ -46,7 +48,9 @@ type SecretName =
| "xhsFeishuAppId"
| "xhsFeishuAppSecret"
| "xhsFeishuAppToken"
| "xhsFeishuTableId";
| "xhsFeishuTableId"
| "feishuMobileAppId"
| "feishuMobileAppSecret";
type KeytarModule = typeof import("keytar");
const KEYTAR_SERVICE = "QianjiangClaw";
......@@ -69,7 +73,9 @@ const KEYTAR_ACCOUNT_MAP: Record<SecretName, string> = {
xhsFeishuAppId: "xhs-feishu-app-id",
xhsFeishuAppSecret: "xhs-feishu-app-secret",
xhsFeishuAppToken: "xhs-feishu-app-token",
xhsFeishuTableId: "xhs-feishu-table-id"
xhsFeishuTableId: "xhs-feishu-table-id",
feishuMobileAppId: "feishu-mobile-app-id",
feishuMobileAppSecret: "feishu-mobile-app-secret"
};
class FileSecretStore implements SecretAccessor {
......@@ -321,6 +327,22 @@ export class SecretManager {
return this.store.get("xhsFeishuTableId");
}
async setFeishuMobileAppId(value?: string): Promise<void> {
await this.store.set("feishuMobileAppId", value);
}
async getFeishuMobileAppId(): Promise<string | undefined> {
return this.store.get("feishuMobileAppId");
}
async setFeishuMobileAppSecret(value?: string): Promise<void> {
await this.store.set("feishuMobileAppSecret", value);
}
async getFeishuMobileAppSecret(): Promise<string | undefined> {
return this.store.get("feishuMobileAppSecret");
}
private async tryLoadKeytar(): Promise<KeytarModule | null> {
try {
const imported = await import("keytar");
......@@ -349,7 +371,9 @@ export class SecretManager {
"xhsFeishuAppId",
"xhsFeishuAppSecret",
"xhsFeishuAppToken",
"xhsFeishuTableId"
"xhsFeishuTableId",
"feishuMobileAppId",
"feishuMobileAppSecret"
] as const) {
const existing = await this.store.get(secretName);
if (existing) {
......
......@@ -82,7 +82,8 @@ import {
getResetDouyinRuntimeSettingsDrafts,
getResetImageSettingsDrafts,
getResetVideoSettingsDrafts,
getResetXhsFeishuSettingsDrafts
getResetXhsFeishuSettingsDrafts,
getResetFeishuMobileSettingsDrafts
} from "./features/settings/settingsDrafts";
import { useSaveSettings } from "./features/settings/useSaveSettings";
import { useSettingsState } from "./features/settings/useSettingsState";
......@@ -425,7 +426,12 @@ export default function App() {
saving,
setSaving,
hasPendingLobsterKey,
hasPendingXhsFeishuConfig
hasPendingXhsFeishuConfig,
feishuMobileAppIdDraft,
setFeishuMobileAppIdDraft,
feishuMobileAppSecretDraft,
setFeishuMobileAppSecretDraft,
hasPendingFeishuMobileConfig
} = useSettingsState(config);
const {
messageTraces,
......@@ -445,6 +451,9 @@ export default function App() {
saveVideoConfig,
saveDigitalHumanConfig,
saveDouyinRuntimeConfig,
saveFeishuMobileConfig,
saveWorkspaceDirectory,
restoreWorkspaceDirectory,
pickWorkspaceDirectory
} = useSaveSettings({
config,
......@@ -478,7 +487,9 @@ export default function App() {
xhsFeishuAppId: xhsFeishuAppIdDraft,
xhsFeishuAppSecret: xhsFeishuAppSecretDraft,
xhsFeishuAppToken: xhsFeishuAppTokenDraft,
xhsFeishuTableId: xhsFeishuTableIdDraft
xhsFeishuTableId: xhsFeishuTableIdDraft,
feishuMobileAppId: feishuMobileAppIdDraft,
feishuMobileAppSecret: feishuMobileAppSecretDraft
},
setters: {
setConfig,
......@@ -512,7 +523,9 @@ export default function App() {
setXhsFeishuAppIdDraft,
setXhsFeishuAppSecretDraft,
setXhsFeishuAppTokenDraft,
setXhsFeishuTableIdDraft
setXhsFeishuTableIdDraft,
setFeishuMobileAppIdDraft,
setFeishuMobileAppSecretDraft
},
labels: {
saveSuccessPending: ui.saveSuccessPending,
......@@ -581,7 +594,8 @@ export default function App() {
hasPendingImageConfig,
hasPendingVideoConfig,
hasPendingDigitalHumanConfig,
hasPendingDouyinRuntimeConfig
hasPendingDouyinRuntimeConfig,
hasPendingFeishuMobileConfig
});
void hasPendingSettingsChange;
const resetXhsFeishuSettingsDrafts = useCallback(() => {
......@@ -591,6 +605,11 @@ export default function App() {
setXhsFeishuAppTokenDraft(drafts.xhsFeishuAppToken);
setXhsFeishuTableIdDraft(drafts.xhsFeishuTableId);
}, []);
const resetFeishuMobileSettingsDrafts = useCallback(() => {
const drafts = getResetFeishuMobileSettingsDrafts();
setFeishuMobileAppIdDraft(drafts.feishuMobileAppId);
setFeishuMobileAppSecretDraft(drafts.feishuMobileAppSecret);
}, []);
const resetCopywritingSettingsDrafts = useCallback(() => {
if (!config) {
return;
......@@ -1562,6 +1581,7 @@ export default function App() {
hasPendingVideoConfig,
hasPendingDigitalHumanConfig,
hasPendingDouyinRuntimeConfig,
hasPendingFeishuMobileConfig,
labels: { export: ui.export },
drafts: {
lobsterKey: lobsterKeyDraft,
......@@ -1590,7 +1610,9 @@ export default function App() {
replicationBriefApiKey: replicationBriefApiKeyDraft,
vectcutBaseUrl: vectcutBaseUrlDraft,
vectcutFileBaseUrl: vectcutFileBaseUrlDraft,
vectcutApiKey: vectcutApiKeyDraft
vectcutApiKey: vectcutApiKeyDraft,
feishuMobileAppId: feishuMobileAppIdDraft,
feishuMobileAppSecret: feishuMobileAppSecretDraft
},
setters: {
setLobsterKey: setLobsterKeyDraft,
......@@ -1619,7 +1641,9 @@ export default function App() {
setReplicationBriefApiKey: setReplicationBriefApiKeyDraft,
setVectcutBaseUrl: setVectcutBaseUrlDraft,
setVectcutFileBaseUrl: setVectcutFileBaseUrlDraft,
setVectcutApiKey: setVectcutApiKeyDraft
setVectcutApiKey: setVectcutApiKeyDraft,
setFeishuMobileAppId: setFeishuMobileAppIdDraft,
setFeishuMobileAppSecret: setFeishuMobileAppSecretDraft
},
onSaveLobsterKey: saveLobsterKey,
onSaveXhsFeishuConfig: saveXhsFeishuConfig,
......@@ -1634,6 +1658,10 @@ export default function App() {
onResetDigitalHumanConfig: resetDigitalHumanSettingsDrafts,
onSaveDouyinRuntimeConfig: saveDouyinRuntimeConfig,
onResetDouyinRuntimeConfig: resetDouyinRuntimeSettingsDrafts,
onSaveFeishuMobileConfig: saveFeishuMobileConfig,
onResetFeishuMobileConfig: resetFeishuMobileSettingsDrafts,
onSaveWorkspaceDirectory: saveWorkspaceDirectory,
onResetWorkspaceDirectory: restoreWorkspaceDirectory,
onRevealSecret: revealConfigSecret,
pickWorkspaceDirectory,
exportDiagnostics
......
import type { ReactNode } from "react"
export interface ModalProps {
open: boolean
onClose: () => void
title?: string
children: ReactNode
}
export function Modal({ open, onClose, title, children }: ModalProps) {
if (!open) {
return null
}
return (
<div className="modal-overlay" role="dialog" aria-modal="true" onClick={onClose}>
<div className="modal-panel" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
{title ? <h2 className="modal-title">{title}</h2> : <span />}
<button
type="button"
className="modal-close-button"
onClick={onClose}
aria-label="关闭"
>
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
<path
d="M18 6L6 18M6 6l12 12"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
</button>
</div>
<div className="modal-body">
{children}
</div>
</div>
</div>
)
}
......@@ -8,6 +8,7 @@ export interface PendingSettingsFlags {
hasPendingVideoConfig: boolean
hasPendingDigitalHumanConfig: boolean
hasPendingDouyinRuntimeConfig: boolean
hasPendingFeishuMobileConfig: boolean
}
export interface BasicResetSettingsDrafts {
......@@ -59,6 +60,11 @@ export interface XhsFeishuResetSettingsDrafts {
xhsFeishuTableId: string
}
export interface FeishuMobileResetSettingsDrafts {
feishuMobileAppId: string
feishuMobileAppSecret: string
}
export function getHasPendingSettingsChange(flags: PendingSettingsFlags) {
return flags.hasPendingBasicConfig
|| flags.hasPendingXhsFeishuConfig
......@@ -67,6 +73,7 @@ export function getHasPendingSettingsChange(flags: PendingSettingsFlags) {
|| flags.hasPendingVideoConfig
|| flags.hasPendingDigitalHumanConfig
|| flags.hasPendingDouyinRuntimeConfig
|| flags.hasPendingFeishuMobileConfig
}
export function getResetBasicSettingsDrafts(config: AppConfig): BasicResetSettingsDrafts {
......@@ -131,3 +138,10 @@ export function getResetXhsFeishuSettingsDrafts(): XhsFeishuResetSettingsDrafts
xhsFeishuTableId: ""
}
}
export function getResetFeishuMobileSettingsDrafts(): FeishuMobileResetSettingsDrafts {
return {
feishuMobileAppId: "",
feishuMobileAppSecret: ""
}
}
......@@ -34,6 +34,8 @@ interface UseSaveSettingsOptions {
xhsFeishuAppSecret: string
xhsFeishuAppToken: string
xhsFeishuTableId: string
feishuMobileAppId: string
feishuMobileAppSecret: string
}
setters: {
setConfig(config: AppConfig): void
......@@ -68,6 +70,8 @@ interface UseSaveSettingsOptions {
setXhsFeishuAppSecretDraft(value: string): void
setXhsFeishuAppTokenDraft(value: string): void
setXhsFeishuTableIdDraft(value: string): void
setFeishuMobileAppIdDraft(value: string): void
setFeishuMobileAppSecretDraft(value: string): void
}
labels: {
saveSuccessPending: string
......@@ -94,6 +98,7 @@ export function useSaveSettings({
expertModelConfig?: SaveConfigInput["expertModelConfig"]
douyinRuntimeConfig?: SaveConfigInput["douyinRuntimeConfig"]
xhsFeishuConfig?: SaveConfigInput["xhsFeishuConfig"]
feishuMobileConfig?: SaveConfigInput["feishuMobileConfig"]
successMessage?: string
resetDrafts?: (savedConfig: AppConfig) => void
}) => {
......@@ -126,6 +131,9 @@ export function useSaveSettings({
if (options?.xhsFeishuConfig) {
input.xhsFeishuConfig = options.xhsFeishuConfig
}
if (options?.feishuMobileConfig) {
input.feishuMobileConfig = options.feishuMobileConfig
}
setters.setSaving(true)
setters.setErrorText("")
......@@ -211,6 +219,19 @@ export function useSaveSettings({
})
}, [drafts.xhsFeishuAppId, drafts.xhsFeishuAppSecret, drafts.xhsFeishuAppToken, drafts.xhsFeishuTableId, saveConfig, setters])
const saveFeishuMobileConfig = useCallback(async () => {
return await saveConfig({
feishuMobileConfig: {
appId: drafts.feishuMobileAppId.trim() || undefined,
appSecret: drafts.feishuMobileAppSecret.trim() || undefined
},
resetDrafts: () => {
setters.setFeishuMobileAppIdDraft("")
setters.setFeishuMobileAppSecretDraft("")
}
})
}, [drafts.feishuMobileAppId, drafts.feishuMobileAppSecret, saveConfig, setters])
const saveCopywritingConfig = useCallback(async () => {
return await saveConfig({
expertModelConfig: {
......@@ -332,6 +353,7 @@ export function useSaveSettings({
saveWorkspaceDirectory,
restoreWorkspaceDirectory,
saveXhsFeishuConfig,
saveFeishuMobileConfig,
saveCopywritingConfig,
saveImageConfig,
saveVideoConfig,
......
......@@ -40,6 +40,8 @@ export function useSettingsState(config: AppConfig | null) {
const [xhsFeishuAppSecretDraft, setXhsFeishuAppSecretDraft] = useState("");
const [xhsFeishuAppTokenDraft, setXhsFeishuAppTokenDraft] = useState("");
const [xhsFeishuTableIdDraft, setXhsFeishuTableIdDraft] = useState("");
const [feishuMobileAppIdDraft, setFeishuMobileAppIdDraft] = useState("");
const [feishuMobileAppSecretDraft, setFeishuMobileAppSecretDraft] = useState("");
const [saving, setSaving] = useState(false);
const hasPendingLobsterKey = lobsterKeyDraft.trim().length > 0;
......@@ -49,6 +51,10 @@ export function useSettingsState(config: AppConfig | null) {
xhsFeishuAppTokenDraft.trim() ||
xhsFeishuTableIdDraft.trim()
);
const hasPendingFeishuMobileConfig = Boolean(
feishuMobileAppIdDraft.trim() ||
feishuMobileAppSecretDraft.trim()
);
const hasPendingModelKeys = Boolean(
imageModelApiKeyDraft.trim()
|| imageModelBaseUrlDraft.trim() !== (config?.expertModelConfig.image.baseUrl ?? "").trim()
......@@ -131,10 +137,15 @@ export function useSettingsState(config: AppConfig | null) {
setXhsFeishuAppTokenDraft,
xhsFeishuTableIdDraft,
setXhsFeishuTableIdDraft,
feishuMobileAppIdDraft,
setFeishuMobileAppIdDraft,
feishuMobileAppSecretDraft,
setFeishuMobileAppSecretDraft,
saving,
setSaving,
hasPendingLobsterKey,
hasPendingXhsFeishuConfig,
hasPendingFeishuMobileConfig,
hasPendingModelKeys
};
}
......@@ -1520,3 +1520,128 @@ select:focus-visible {
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
/* Modal */
.modal-overlay {
position: fixed;
inset: 0;
z-index: 100;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.28);
animation: modal-overlay-in 0.15s ease-out;
}
@keyframes modal-overlay-in {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-panel {
background: #ffffff;
border: 1px solid #e8ecf1;
border-radius: 12px;
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.14);
max-width: 560px;
width: calc(100vw - 48px);
max-height: calc(100vh - 80px);
display: flex;
flex-direction: column;
animation: modal-panel-in 0.18s ease-out;
}
@keyframes modal-panel-in {
from { opacity: 0; transform: translateY(8px) scale(0.98); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 18px 22px 0;
}
.modal-title {
font-size: 15px;
font-weight: 600;
color: var(--color-text-primary, #1a1a2e);
margin: 0;
}
.modal-close-button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 6px;
border: none;
background: transparent;
color: #94a3b8;
cursor: pointer;
flex-shrink: 0;
}
.modal-close-button:hover {
background: rgba(0, 0, 0, 0.06);
color: #1e293b;
}
.modal-body {
padding: 16px 22px 22px;
font-size: 13px;
line-height: 1.7;
color: var(--color-text-secondary, #62759a);
overflow-y: auto;
}
.modal-body ol {
margin: 8px 0 0;
padding-left: 18px;
}
.modal-body li {
margin-bottom: 4px;
}
.modal-body .modal-manual-footer {
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid var(--color-border, rgba(0, 0, 0, 0.08));
color: var(--color-text-tertiary, #94a3b8);
font-size: 12px;
}
.settings-manual-link {
display: inline-flex;
align-items: center;
gap: 4px;
background: none;
border: none;
color: var(--color-accent, #3b82f6);
font-size: 12px;
cursor: pointer;
padding: 2px 6px;
border-radius: 6px;
}
.settings-manual-link:hover {
background: rgba(59, 130, 246, 0.08);
text-decoration: underline;
}
.settings-section-title-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
.settings-section-title {
font-size: 13px;
font-weight: 600;
color: var(--color-text-primary, #1a1a2e);
margin: 0;
}
This diff is collapsed.
......@@ -105,7 +105,9 @@ export type ConfigSecretId =
| "xhsFeishuAppId"
| "xhsFeishuAppSecret"
| "xhsFeishuAppToken"
| "xhsFeishuTableId";
| "xhsFeishuTableId"
| "feishuMobileAppId"
| "feishuMobileAppSecret";
export interface WorkspaceWarmupResult {
accepted: boolean;
......@@ -660,6 +662,11 @@ export interface XhsFeishuConfig {
tableIdConfigured: boolean;
}
export interface FeishuMobileConfig {
appIdConfigured: boolean;
appSecretConfigured: boolean;
}
export const FIXED_EXPERT_MODEL_ENDPOINTS = {
copywriting: {
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
......@@ -717,6 +724,7 @@ export interface AppConfig {
expertModelConfig: ExpertModelConfig;
douyinRuntimeConfig: DouyinRuntimeConfig;
xhsFeishuConfig: XhsFeishuConfig;
feishuMobileConfig: FeishuMobileConfig;
}
export interface DiagnosticsExportResult {
......@@ -757,6 +765,11 @@ export interface XhsFeishuConfigInput {
tableId?: string;
}
export interface FeishuMobileConfigInput {
appId?: string;
appSecret?: string;
}
export interface SaveConfigInput {
setupMode: SetupMode;
provider: string;
......@@ -782,6 +795,7 @@ export interface SaveConfigInput {
vectcut?: VectCutModelInput;
};
xhsFeishuConfig?: XhsFeishuConfigInput;
feishuMobileConfig?: FeishuMobileConfigInput;
}
export interface AuthSessionSummary {
......
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