Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qjclaw-dmg
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
AI-甘富林
qjclaw-dmg
Commits
dbd31e98
Commit
dbd31e98
authored
Apr 22, 2026
by
AI-甘富林
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(settings): support workspace directory switching
parent
2c7fc20a
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
241 additions
and
151 deletions
+241
-151
ipc.ts
apps/desktop/src/main/ipc.ts
+168
-129
App.tsx
apps/ui/src/App.tsx
+71
-22
index.ts
packages/shared-types/src/index.ts
+2
-0
No files found.
apps/desktop/src/main/ipc.ts
View file @
dbd31e98
...
...
@@ -131,6 +131,23 @@ function sanitizeAttachmentFileComponent(value: string): string {
return
sanitized
||
"attachment"
;
}
function
normalizeComparablePath
(
value
?:
string
):
string
{
const
trimmed
=
value
?.
trim
();
if
(
!
trimmed
)
{
return
""
;
}
try
{
return
path
.
resolve
(
trimmed
).
replace
(
/
[\\/]
+/g
,
"
\
\"
).toLowerCase();
} catch {
return trimmed.replace(/[
\\
/]+/g, "
\\
").toLowerCase();
}
}
function didWorkspacePathChange(previousConfig: AppConfig, nextConfig: AppConfig): boolean {
return normalizeComparablePath(previousConfig.workspacePath) !== normalizeComparablePath(nextConfig.workspacePath);
}
async function pickImageAttachment(window: BrowserWindow | null): Promise<ChatAttachment | null> {
const dialogOptions: OpenDialogOptions = {
title: "
Select
image
",
...
...
@@ -173,6 +190,24 @@ async function pickImageAttachment(window: BrowserWindow | null): Promise<ChatAt
};
}
async function pickWorkspaceDirectory(window: BrowserWindow | null, currentPath?: string): Promise<string | null> {
const defaultPath = currentPath?.trim();
const dialogOptions: OpenDialogOptions = {
title: "
Select
workspace
directory
",
properties: ["
openDirectory
", "
createDirectory
"],
...(defaultPath ? { defaultPath } : {})
};
const result = window
? await dialog.showOpenDialog(window, dialogOptions)
: await dialog.showOpenDialog(dialogOptions);
if (result.canceled || !result.filePaths.length) {
return null;
}
const selectedPath = result.filePaths[0]?.trim();
return selectedPath || null;
}
function normalizeChatAttachments(attachments?: ChatAttachment[]): ChatAttachment[] {
if (!Array.isArray(attachments)) {
return [];
...
...
@@ -335,6 +370,7 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
runtimeManager,
profileClient,
secretManager,
skillStore,
skillClient,
expertCatalogService,
skillCatalogService,
...
...
@@ -585,6 +621,44 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
})();
};
const syncCachedRemoteProjectsToWorkspace = async (
reason: string,
options: {
action?: RuntimeCloudFetchAction;
config?: AppConfig;
} = {}
): Promise<void> => {
const nextConfig = options.config ?? await configService.load();
if (nextConfig.setupMode !== "
employee
-
key
") {
return;
}
const apiKey = (await secretManager.getApiKey())?.trim();
if (!apiKey) {
return;
}
let runtimeCloudStatus = await runtimeCloudClient.getStatus();
let remoteSkills = runtimeCloudClient.getRemoteSkillAssets();
if (!runtimeCloudStatus.config || remoteSkills.length === 0) {
runtimeCloudStatus = await runtimeCloudClient.fetchConfig(options.action ?? "
init
");
remoteSkills = runtimeCloudClient.getRemoteSkillAssets();
}
if (!runtimeCloudStatus.config) {
return;
}
await skillStore.reconcile(remoteSkills, runtimeCloudStatus.config.configVersion);
await projectBundleService.syncRemoteBundles(remoteSkills, runtimeCloudStatus.config.configVersion, options.action ?? "
sync
");
await startupLogger.info("
project
-
bundle
", "
workspace
-
project
-
sync
", "
Synced
cached
runtime
cloud
projects
into
the
current
workspace
root
.
", {
reason,
workspacePath: nextConfig.workspacePath,
remoteSkillCount: remoteSkills.length,
configVersion: runtimeCloudStatus.config.configVersion
});
};
const startManagedRuntime = async (
reason: string,
options: {
...
...
@@ -728,6 +802,95 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
};
};
const saveAppConfig = async (input: SaveConfigInput): Promise<AppConfig> => {
const previousConfig = await configService.load();
const config = await configService.save(input);
const workspacePathChanged = didWorkspacePathChange(previousConfig, config);
const shouldSyncChatManagedConfig = config.setupMode === "
employee
-
key
" && config.runtimeMode !== "
external
-
gateway
" && (
typeof input.expertModelConfig?.copywriting?.baseUrl === "
string
"
|| typeof input.expertModelConfig?.copywriting?.apiKey === "
string
"
|| typeof input.expertModelConfig?.copywriting?.modelId === "
string
"
);
if (typeof input.apiKey === "
string
") {
await secretManager.setApiKey(input.apiKey || undefined);
}
if (typeof input.gatewayToken === "
string
") {
await secretManager.setGatewayToken(input.gatewayToken || undefined);
}
if (typeof input.authToken === "
string
") {
await secretManager.setAuthToken(input.authToken || undefined);
}
if (typeof input.expertModelConfig?.image?.apiKey === "
string
") {
await secretManager.setImageModelApiKey(input.expertModelConfig.image.apiKey || undefined);
}
if (typeof input.expertModelConfig?.video?.apiKey === "
string
") {
await secretManager.setVideoModelApiKey(input.expertModelConfig.video.apiKey || undefined);
}
if (typeof input.expertModelConfig?.copywriting?.apiKey === "
string
") {
await secretManager.setCopywritingModelApiKey(input.expertModelConfig.copywriting.apiKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.volcAccessKey === "
string
") {
await secretManager.setDigitalHumanVolcAccessKey(input.expertModelConfig.digitalHuman.volcAccessKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.volcSecretKey === "
string
") {
await secretManager.setDigitalHumanVolcSecretKey(input.expertModelConfig.digitalHuman.volcSecretKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.qiniuAccessKey === "
string
") {
await secretManager.setDigitalHumanQiniuAccessKey(input.expertModelConfig.digitalHuman.qiniuAccessKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.qiniuSecretKey === "
string
") {
await secretManager.setDigitalHumanQiniuSecretKey(input.expertModelConfig.digitalHuman.qiniuSecretKey || undefined);
}
if (
config.setupMode === "
direct
-
provider
"
|| previousConfig.setupMode !== config.setupMode
|| previousConfig.runtimeCloudApiBaseUrl !== config.runtimeCloudApiBaseUrl
|| (config.setupMode === "
employee
-
key
"
&& typeof input.apiKey === "
string
"
&& input.apiKey.trim().length > 0)
) {
await runtimeCloudClient.clearCache().catch(() => undefined);
}
await runtimeManager.setRequestedMode(config.runtimeMode);
if (shouldSyncChatManagedConfig) {
await runtimeManager.syncManagedConfig("
sync
");
}
if (workspacePathChanged) {
await syncCachedRemoteProjectsToWorkspace("
config
-
save
", {
action: "
init
",
config
}).catch(async (error) => {
const message = error instanceof Error ? error.message : String(error);
console.error("
[
workspace
-
config
]
", "
workspace
-
project
-
sync
.
error
", message);
await startupLogger.error("
project
-
bundle
", "
workspace
-
project
-
sync
.
error
", "
Failed
to
sync
cached
runtime
cloud
projects
after
workspace
path
change
.
", {
workspacePath: config.workspacePath,
error: message
});
});
}
if (config.runtimeMode !== "
external
-
gateway
" && (await secretManager.getApiKey())) {
await reconfigureGatewayClient(config, input.gatewayToken);
void queueWorkspaceWarmup("
config
-
save
", {
action: "
init
",
config,
inputToken: input.gatewayToken,
restart: config.setupMode === "
employee
-
key
"
&& typeof input.apiKey === "
string
"
&& input.apiKey.trim().length > 0
});
} else {
await runtimeCloudSupervisor.stop("
config
-
save
");
await reconfigureGatewayClient(config, input.gatewayToken);
if (config.setupMode === "
employee
-
key
") {
await syncRuntimeCloudSupervisor("
config
-
save
");
}
}
void dailyReportService.runDueCheck().catch(() => undefined);
return getEffectiveConfig();
};
const buildWorkspaceSummary = async (): Promise<WorkspaceSummary> => {
const config = await getEffectiveConfig();
await projectStore.initialize();
...
...
@@ -1625,79 +1788,10 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
ipcMain.handle(IPC_CHANNELS.runtimeTelemetryGetStatus, async () => runtimeCloudSupervisor.getStatus());
ipcMain.handle(IPC_CHANNELS.configLoad, async () => getEffectiveConfig());
ipcMain.handle(IPC_CHANNELS.configSave, async (_event, input: SaveConfigInput) => {
const previousConfig = await configService.load();
const config = await configService.save(input);
const shouldSyncChatManagedConfig = config.setupMode === "employee-key" && config.runtimeMode !== "external-gateway" && (
typeof input.expertModelConfig?.copywriting?.baseUrl === "string"
|| typeof input.expertModelConfig?.copywriting?.apiKey === "string"
|| typeof input.expertModelConfig?.copywriting?.modelId === "string"
);
if (typeof input.apiKey === "string") {
await secretManager.setApiKey(input.apiKey || undefined);
}
if (typeof input.gatewayToken === "string") {
await secretManager.setGatewayToken(input.gatewayToken || undefined);
}
if (typeof input.authToken === "string") {
await secretManager.setAuthToken(input.authToken || undefined);
}
if (typeof input.expertModelConfig?.image?.apiKey === "string") {
await secretManager.setImageModelApiKey(input.expertModelConfig.image.apiKey || undefined);
}
if (typeof input.expertModelConfig?.video?.apiKey === "string") {
await secretManager.setVideoModelApiKey(input.expertModelConfig.video.apiKey || undefined);
}
if (typeof input.expertModelConfig?.copywriting?.apiKey === "string") {
await secretManager.setCopywritingModelApiKey(input.expertModelConfig.copywriting.apiKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.volcAccessKey === "string") {
await secretManager.setDigitalHumanVolcAccessKey(input.expertModelConfig.digitalHuman.volcAccessKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.volcSecretKey === "string") {
await secretManager.setDigitalHumanVolcSecretKey(input.expertModelConfig.digitalHuman.volcSecretKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.qiniuAccessKey === "string") {
await secretManager.setDigitalHumanQiniuAccessKey(input.expertModelConfig.digitalHuman.qiniuAccessKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.qiniuSecretKey === "string") {
await secretManager.setDigitalHumanQiniuSecretKey(input.expertModelConfig.digitalHuman.qiniuSecretKey || undefined);
}
if (
config.setupMode === "direct-provider"
|| previousConfig.setupMode !== config.setupMode
|| previousConfig.runtimeCloudApiBaseUrl !== config.runtimeCloudApiBaseUrl
|| (config.setupMode === "employee-key"
&& typeof input.apiKey === "string"
&& input.apiKey.trim().length > 0)
) {
await runtimeCloudClient.clearCache().catch(() => undefined);
}
await runtimeManager.setRequestedMode(config.runtimeMode);
if (shouldSyncChatManagedConfig) {
await runtimeManager.syncManagedConfig("sync");
}
if (config.runtimeMode !== "external-gateway" && (await secretManager.getApiKey())) {
await reconfigureGatewayClient(config, input.gatewayToken);
void queueWorkspaceWarmup("config-save", {
action: "init",
config,
inputToken: input.gatewayToken,
restart: config.setupMode === "employee-key"
&& typeof input.apiKey === "string"
&& input.apiKey.trim().length > 0
});
} else {
await runtimeCloudSupervisor.stop("config-save");
await reconfigureGatewayClient(config, input.gatewayToken);
if (config.setupMode === "employee-key") {
await syncRuntimeCloudSupervisor("config-save");
}
}
void dailyReportService.runDueCheck().catch(() => undefined);
return getEffectiveConfig();
ipcMain.handle(IPC_CHANNELS.configPickWorkspaceDirectory, async (event, currentPath?: string) => {
return pickWorkspaceDirectory(BrowserWindow.fromWebContents(event.sender), currentPath);
});
ipcMain.handle(IPC_CHANNELS.configSave, async (_event, input: SaveConfigInput) => saveAppConfig(input));
ipcMain.handle(IPC_CHANNELS.authGetSession, async () => authClient.getSessionSummary());
ipcMain.handle(IPC_CHANNELS.authSignIn, async (_event, input: SignInInput) => {
...
...
@@ -1834,63 +1928,8 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
},
config: {
load: () => getEffectiveConfig(),
save: async (input: SaveConfigInput) => {
const config = await configService.save(input);
const shouldSyncChatManagedConfig = config.setupMode === "employee-key" && config.runtimeMode !== "external-gateway" && (
typeof input.expertModelConfig?.copywriting?.baseUrl === "string"
|| typeof input.expertModelConfig?.copywriting?.apiKey === "string"
|| typeof input.expertModelConfig?.copywriting?.modelId === "string"
);
if (typeof input.apiKey === "string") {
await secretManager.setApiKey(input.apiKey || undefined);
}
if (typeof input.gatewayToken === "string") {
await secretManager.setGatewayToken(input.gatewayToken || undefined);
}
if (typeof input.authToken === "string") {
await secretManager.setAuthToken(input.authToken || undefined);
}
if (typeof input.expertModelConfig?.image?.apiKey === "string") {
await secretManager.setImageModelApiKey(input.expertModelConfig.image.apiKey || undefined);
}
if (typeof input.expertModelConfig?.video?.apiKey === "string") {
await secretManager.setVideoModelApiKey(input.expertModelConfig.video.apiKey || undefined);
}
if (typeof input.expertModelConfig?.copywriting?.apiKey === "string") {
await secretManager.setCopywritingModelApiKey(input.expertModelConfig.copywriting.apiKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.volcAccessKey === "string") {
await secretManager.setDigitalHumanVolcAccessKey(input.expertModelConfig.digitalHuman.volcAccessKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.volcSecretKey === "string") {
await secretManager.setDigitalHumanVolcSecretKey(input.expertModelConfig.digitalHuman.volcSecretKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.qiniuAccessKey === "string") {
await secretManager.setDigitalHumanQiniuAccessKey(input.expertModelConfig.digitalHuman.qiniuAccessKey || undefined);
}
if (typeof input.expertModelConfig?.digitalHuman?.qiniuSecretKey === "string") {
await secretManager.setDigitalHumanQiniuSecretKey(input.expertModelConfig.digitalHuman.qiniuSecretKey || undefined);
}
await runtimeManager.setRequestedMode(config.runtimeMode);
if (shouldSyncChatManagedConfig) {
await runtimeManager.syncManagedConfig("sync");
}
if (config.runtimeMode !== "external-gateway" && (await secretManager.getApiKey())) {
await reconfigureGatewayClient(config, input.gatewayToken);
void queueWorkspaceWarmup("config-save", {
action: "init",
config,
inputToken: input.gatewayToken
});
} else {
await runtimeCloudSupervisor.stop("config-save");
await reconfigureGatewayClient(config, input.gatewayToken);
await syncRuntimeCloudSupervisor("config-save");
}
void dailyReportService.runDueCheck().catch(() => undefined);
return getEffectiveConfig();
}
pickWorkspaceDirectory: (currentPath?: string) => pickWorkspaceDirectory(null, currentPath),
save: (input: SaveConfigInput) => saveAppConfig(input)
},
projects: {
list: () => projectStore.listProjects(),
...
...
apps/ui/src/App.tsx
View file @
dbd31e98
...
...
@@ -948,6 +948,7 @@ const mockDesktopApi = {
}
}
}),
pickWorkspaceDirectory
:
async
(
currentPath
?:
string
)
=>
currentPath
||
"D:/workspace"
,
save
:
async
(
input
:
SaveConfigInput
)
=>
({
setupMode
:
input
.
setupMode
,
provider
:
input
.
provider
,
...
...
@@ -2036,6 +2037,7 @@ export default function App() {
const copiedTokenResetRef = useRef<number | null>(null);
const composerDragDepthRef = useRef(0);
const startupWarmupRequestedRef = useRef(false);
const lastLoadedWorkspacePathRef = useRef<string | null>(null);
const [streamSmoke, setStreamSmoke] = useState<SmokeStreamSnapshot | null>(null);
const minimizeWindow = () => void desktopApi.window.minimize();
const maximizeWindow = () => void desktopApi.window.maximize();
...
...
@@ -2044,6 +2046,9 @@ export default function App() {
const effectiveSkills = useMemo(() => (readySkills.length ? [DEFAULT_SKILL, ...readySkills] : [DEFAULT_SKILL]), [readySkills]);
const selectedSkill = useMemo(() => effectiveSkills.find((skill) => skill.id === selectedSkillId) ?? effectiveSkills[0] ?? DEFAULT_SKILL, [effectiveSkills, selectedSkillId]);
const setupMode = workspace?.setupMode ?? config?.setupMode ?? "employee-key";
const savedWorkspacePath = config?.workspacePath ?? "";
const displayedWorkspacePath = workspacePathDraft.trim() || savedWorkspacePath || ui.none;
const hasPendingWorkspacePathChange = Boolean(config && workspacePathDraft.trim() && workspacePathDraft.trim() !== savedWorkspacePath);
const shellReady = workspace?.shellReady ?? false;
const bindingRequired = workspace?.bindingRequired ?? !Boolean(workspace?.apiKeyConfigured ?? config?.apiKeyConfigured);
const chatLaunchState: ChatLaunchState = workspace?.chatLaunchState ?? (!bindingRequired ? "starting" : "unbound");
...
...
@@ -2305,7 +2310,6 @@ export default function App() {
setRuntimeTelemetry(nextTelemetry);
setSystemSummary(nextSystem);
setExpertDefinitions(nextExperts);
setWorkspacePathDraft((current) => current || nextConfig.workspacePath);
setGatewayStatus(statusResult);
const nextReadySkills = nextWorkspace.skills.filter((skill) => skill.ready);
...
...
@@ -2504,7 +2508,14 @@ export default function App() {
return;
}
setWorkspacePathDraft(config.workspacePath);
setWorkspacePathDraft((current) => {
const lastLoadedWorkspacePath = lastLoadedWorkspacePathRef.current;
lastLoadedWorkspacePathRef.current = config.workspacePath;
if (!current || current === lastLoadedWorkspacePath) {
return config.workspacePath;
}
return current;
});
setImageModelBaseUrlDraft(config.expertModelConfig.image.baseUrl);
setImageModelIdDraft(config.expertModelConfig.image.modelId ?? FIXED_EXPERT_MODEL_ENDPOINTS.image.modelId);
setVideoModelBaseUrlDraft(config.expertModelConfig.video.baseUrl);
...
...
@@ -3381,6 +3392,7 @@ export default function App() {
lobsterKey?: string;
workspacePath?: string;
expertModelConfig?: SaveConfigInput["expertModelConfig"];
successMessage?: string;
}) {
if (!config) {
return;
...
...
@@ -3435,7 +3447,7 @@ export default function App() {
setDigitalHumanVolcSecretKeyDraft("");
setDigitalHumanQiniuAccessKeyDraft("");
setDigitalHumanQiniuSecretKeyDraft("");
setInfoText(
trimmedLobsterKey ? ui.saveSuccessPending : ui.saveSuccessApplied
);
setInfoText(
options?.successMessage ?? (trimmedLobsterKey ? ui.saveSuccessPending : ui.saveSuccessApplied)
);
void refresh(false);
} catch (error) {
setErrorText(err(error));
...
...
@@ -4079,6 +4091,46 @@ export default function App() {
}
}
async function pickWorkspaceDirectory() {
if (!config || saving) {
return;
}
setErrorText("");
try {
const nextPath = await desktopApi.config.pickWorkspaceDirectory(workspacePathDraft.trim() || config.workspacePath);
if (!nextPath?.trim()) {
return;
}
setWorkspacePathDraft(nextPath.trim());
setInfoText("");
} catch (error) {
setErrorText(err(error));
}
}
function restoreWorkspaceDirectory() {
if (!config) {
return;
}
setWorkspacePathDraft(config.workspacePath);
setErrorText("");
setInfoText("");
}
async function saveWorkspaceDirectory() {
if (!hasPendingWorkspacePathChange) {
return;
}
await saveConfig({
workspacePath: workspacePathDraft,
successMessage: "工作目录已保存,正在重新预热工作区。"
});
}
const sidebarSessionLabel = viewMode === "experts" ? "专家会话" : "会话管理";
const selectedSkillBadge = selectedSkillId === DEFAULT_SKILL.id ? "千匠问天" : "@" + selectedSkill.name;
const sidebarNewSessionAction = (
...
...
@@ -4905,30 +4957,27 @@ export default function App() {
<div className="settings-section-headline">
<div>
<span className="settings-section-kicker">诊断与工作区</span>
<h4>{ui.diagnostics}</h4>
<p>{ui.diagnosticsDesc}</p>
</div>
</div>
<div className="settings-static-list">
<div className="settings-static-item">
<span>{ui.workspacePath}</span>
<strong>{config?.workspacePath || workspacePathDraft || ui.none}</strong>
<div className="workspace-directory-card">
<div className="workspace-directory-panel">
<span className="workspace-directory-eyebrow">当前生效目录</span>
<strong className="workspace-directory-path">{displayedWorkspacePath}</strong>
<p className="workspace-directory-hint">项目会从该目录加载;保存后会重新预热工作区。</p>
</div>
{hasPendingWorkspacePathChange ? (
<div className="workspace-directory-draft-row">
<span className="workspace-directory-draft-badge">待保存</span>
<div className="workspace-directory-inline-actions">
<button disabled={saving} onClick={() => void saveWorkspaceDirectory()}>{saving ? ui.saving : ui.save}</button>
<button className="secondary workspace-directory-inline-button" disabled={saving} onClick={restoreWorkspaceDirectory}>恢复当前</button>
</div>
</div>
<div className="diagnostic-meta-list">
{runtimeCloudStatus ? (
<>
<div className="mini-info"><span>Runtime Cloud Target</span><strong>{runtimeCloudStatus.baseUrl || ui.none}</strong></div>
<div className="mini-info"><span>Target Source</span><strong>{runtimeCloudStatus.baseUrlSource ?? ui.none}</strong></div>
<div className="mini-info"><span>Binding Host</span><strong>{runtimeCloudStatus.baseHost ?? ui.none}</strong></div>
</>
) : null}
</div>
<div className="button-row settings-actions">
<button className="secondary" onClick={() => void handleCopyText("workspace-path", config?.workspacePath || workspacePathDraft || ui.none)}>
{copiedToken === "workspace-path" ? ui.copied : ui.copy}
</button>
<button className="secondary" onClick={() => void exportDiagnostics()}>{ui.export}</button>
<div className="button-row settings-actions workspace-directory-actions">
<button disabled={saving || !config} onClick={() => void pickWorkspaceDirectory()}>更改目录</button>
<button className="secondary" disabled={saving} onClick={() => void exportDiagnostics()}>{ui.export}</button>
</div>
</div>
</section>
...
...
packages/shared-types/src/index.ts
View file @
dbd31e98
...
...
@@ -20,6 +20,7 @@
runtimeCloudFetchConfig
:
"runtime-cloud:fetch-config"
,
runtimeTelemetryGetStatus
:
"runtime-telemetry:get-status"
,
configLoad
:
"config:load"
,
configPickWorkspaceDirectory
:
"config:pick-workspace-directory"
,
configSave
:
"config:save"
,
projectsList
:
"projects:list"
,
projectsSetActive
:
"projects:set-active"
,
...
...
@@ -802,6 +803,7 @@ export interface DesktopApi {
};
config
:
{
load
():
Promise
<
AppConfig
>
;
pickWorkspaceDirectory
(
currentPath
?:
string
):
Promise
<
string
|
null
>
;
save
(
input
:
SaveConfigInput
):
Promise
<
AppConfig
>
;
};
projects
:
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment