improve(settings): rework about support info and debug log layout
This commit is contained in:
13 files changed
+152
-41
No files matched your search
@@ -156,6 +156,7 @@ import {
|
||||
webdavSyncSecretsStatus,
|
||||
webdavSyncTest,
|
||||
webdavSyncUpload,
|
||||
listInstalledAgentsLocal,
|
||||
type AppSupportInfo,
|
||||
type McpHttpServerSettings,
|
||||
type McpHttpServerStatus,
|
||||
@@ -2229,9 +2230,30 @@ const appSupportInfoLabels = computed<AppSupportInfoLabels>(() => ({
|
||||
runtimeWeb: t("settings.supportInfoRuntimeWeb"),
|
||||
operatingSystem: t("settings.supportInfoOperatingSystem"),
|
||||
architecture: t("settings.supportInfoArchitecture"),
|
||||
userAgent: t("settings.supportInfoUserAgent"),
|
||||
databaseTypes: t("settings.supportInfoDatabaseTypes"),
|
||||
localDriverVersions: t("settings.supportInfoLocalDriverVersions"),
|
||||
aiProviders: t("settings.supportInfoAiProviders"),
|
||||
unknown: t("settings.supportInfoUnknown"),
|
||||
}));
|
||||
const appSupportInfoRows = computed(() => (appSupportInfo.value ? buildAppSupportInfoRows(appSupportInfo.value, appSupportInfoLabels.value) : []));
|
||||
const appSupportInfoRows = computed(() => {
|
||||
if (!appSupportInfo.value) return [];
|
||||
const info = appSupportInfo.value;
|
||||
return buildAppSupportInfoRows(info, appSupportInfoLabels.value).map((row) => {
|
||||
const details: string[] = [];
|
||||
if (row.key === "appVersion") {
|
||||
if (info.databaseTypes?.length) details.push(`${appSupportInfoLabels.value.databaseTypes}: ${info.databaseTypes.join(", ")}`);
|
||||
if (info.localDriverVersions?.length) {
|
||||
details.push(`${appSupportInfoLabels.value.localDriverVersions}: ${info.localDriverVersions.map((driver) => `${driver.dbType} ${driver.version}`).join(", ")}`);
|
||||
}
|
||||
}
|
||||
if (row.key === "operatingSystem") {
|
||||
if (info.userAgent?.trim()) details.push(`${appSupportInfoLabels.value.userAgent}: ${info.userAgent.trim()}`);
|
||||
if (info.aiProviders?.length) details.push(`${appSupportInfoLabels.value.aiProviders}: ${info.aiProviders.join(", ")}`);
|
||||
}
|
||||
return { ...row, tooltip: details.join("\n") };
|
||||
});
|
||||
});
|
||||
const settingsCategoryNav = computed<{ value: SettingsCategory; label: string }[]>(() => [
|
||||
{ value: "appearance", label: t("settings.appearanceTab") },
|
||||
{ value: "editor", label: t("settings.editorTab") },
|
||||
@@ -2457,7 +2479,33 @@ async function refreshAppSupportInfo() {
|
||||
appSupportInfoLoading.value = true;
|
||||
appSupportInfoError.value = "";
|
||||
try {
|
||||
appSupportInfo.value = await getAppSupportInfo();
|
||||
const [info, localDrivers] = await Promise.all([getAppSupportInfo(), listInstalledAgentsLocal().catch(() => [])]);
|
||||
const databaseTypes = [
|
||||
...new Set(
|
||||
connectionStore.connections
|
||||
.map((connection) => {
|
||||
const dbType = connection.db_type.trim();
|
||||
const profile = connection.driver_profile?.trim();
|
||||
return profile && profile !== dbType ? `${dbType}/${profile}` : dbType;
|
||||
})
|
||||
.filter(Boolean),
|
||||
),
|
||||
].sort();
|
||||
const localDriverVersions = localDrivers
|
||||
.filter((driver) => driver.installed && driver.installed_version?.trim())
|
||||
.map((driver) => ({ dbType: driver.db_type, version: driver.installed_version!.trim() }))
|
||||
.sort((a, b) => a.dbType.localeCompare(b.dbType));
|
||||
const aiProviders = [
|
||||
...new Set(
|
||||
settingsStore.aiConfigs
|
||||
.filter((config) => {
|
||||
const preset = getAiProviderPreset(config.provider, config.endpoint);
|
||||
return config.endpoint.trim() && config.model.trim() && (!preset.requiresApiKey || config.apiKey.trim());
|
||||
})
|
||||
.map((config) => getAiProviderPreset(config.provider, config.endpoint).label),
|
||||
),
|
||||
].sort();
|
||||
appSupportInfo.value = { ...info, databaseTypes, localDriverVersions, aiProviders };
|
||||
} catch (e: any) {
|
||||
appSupportInfo.value = appSupportInfo.value || fallbackAppSupportInfo();
|
||||
appSupportInfoError.value = e?.message || String(e);
|
||||
@@ -6164,29 +6212,6 @@ onUnmounted(() => {
|
||||
<Switch id="update-notifications-enabled" v-model="editUpdateNotificationsEnabled" />
|
||||
</div>
|
||||
|
||||
<div v-if="!isWeb" class="settings-item flex flex-col gap-3 rounded-md border bg-muted/20 px-3 py-2">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="space-y-1">
|
||||
<Label for="debug-logging-enabled">{{ t("settings.debugLoggingEnabled") }}</Label>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ t("settings.debugLoggingEnabledDescription") }}
|
||||
</p>
|
||||
</div>
|
||||
<Switch id="debug-logging-enabled" v-model="editDebugLoggingEnabled" />
|
||||
</div>
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button type="button" variant="outline" size="sm" @click="clearDebugLogs">
|
||||
{{ t("settings.debugLogsClear") }}
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="sm" @click="copyDebugLogs">
|
||||
{{ debugLogCopied ? t("settings.debugLogsCopied") : t("settings.debugLogsCopy") }}
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="sm" @click="exportDebugLogs">
|
||||
{{ debugLogDownloaded ? t("settings.debugLogsDownloaded") : t("settings.debugLogsDownload") }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-appearance-group" data-icon-theme-settings>
|
||||
<Label>{{ t("settings.iconTheme") }}</Label>
|
||||
<div class="settings-appearance-choice-grid settings-icon-theme-grid">
|
||||
@@ -9296,16 +9321,25 @@ LIMIT 100;</pre
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="appSupportInfoRows.length" class="mt-4 grid gap-3 sm:grid-cols-2">
|
||||
<div v-for="row in appSupportInfoRows" :key="row.key" class="min-w-0 rounded-md bg-muted/30 px-3 py-2">
|
||||
<div class="text-xs font-medium text-muted-foreground">
|
||||
{{ row.label }}
|
||||
</div>
|
||||
<div class="mt-1 min-w-0 select-text break-words font-mono text-xs text-foreground">
|
||||
{{ row.value }}
|
||||
</div>
|
||||
<TooltipProvider v-if="appSupportInfoRows.length">
|
||||
<div class="mt-4 grid gap-3 sm:grid-cols-4">
|
||||
<Tooltip v-for="row in appSupportInfoRows" :key="row.key">
|
||||
<TooltipTrigger as-child>
|
||||
<div class="min-w-0 rounded-md bg-muted/30 px-3 py-2" :class="row.tooltip ? 'cursor-help' : ''">
|
||||
<div class="text-xs font-medium text-muted-foreground">
|
||||
{{ row.label }}
|
||||
</div>
|
||||
<div class="mt-1 min-w-0 select-text break-words font-mono text-xs text-foreground">
|
||||
{{ row.value }}
|
||||
</div>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent v-if="row.tooltip" class="max-w-[520px] whitespace-pre-line break-words text-xs leading-relaxed" side="top" align="start" :side-offset="8">
|
||||
{{ row.tooltip }}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
<p v-else class="mt-4 text-sm text-muted-foreground">
|
||||
{{ t("settings.supportInfoLoading") }}
|
||||
</p>
|
||||
@@ -9326,7 +9360,28 @@ LIMIT 100;</pre
|
||||
:get-draft-conflict-categories="getDraftConflictCategories"
|
||||
/>
|
||||
|
||||
<ChangelogPanel :checking-updates="props.checkingUpdates" @check-updates="emit('check-updates')" />
|
||||
<div v-if="!isWeb" class="settings-item flex flex-col gap-3 rounded-md border bg-muted/20 px-3 py-2">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="space-y-1">
|
||||
<Label for="debug-logging-enabled">{{ t("settings.debugLoggingEnabled") }}</Label>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ t("settings.debugLoggingEnabledDescription") }}
|
||||
</p>
|
||||
</div>
|
||||
<Switch id="debug-logging-enabled" v-model="editDebugLoggingEnabled" />
|
||||
</div>
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button type="button" variant="outline" size="sm" @click="clearDebugLogs">
|
||||
{{ t("settings.debugLogsClear") }}
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="sm" @click="copyDebugLogs">
|
||||
{{ debugLogCopied ? t("settings.debugLogsCopied") : t("settings.debugLogsCopy") }}
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="sm" @click="exportDebugLogs">
|
||||
{{ debugLogDownloaded ? t("settings.debugLogsDownloaded") : t("settings.debugLogsDownload") }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-item rounded-lg border p-4">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
@@ -9348,6 +9403,8 @@ LIMIT 100;</pre
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ChangelogPanel :checking-updates="props.checkingUpdates" @check-updates="emit('check-updates')" />
|
||||
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<button type="button" class="rounded-lg border p-4 text-left transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" @click="openExternalUrl('https://qm.qq.com/cgi-bin/qm/qr?k=&group_code=1087880322')">
|
||||
<div class="text-xs font-medium uppercase tracking-wider text-muted-foreground">
|
||||
|
||||
+2
-2
@@ -128,8 +128,8 @@ describe("EditorSettingsDialog choice card containment", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("restores large icon theme choices below the debug log controls", () => {
|
||||
expect(sourceIndexForKey("iconTheme")).toBeGreaterThan(sourceIndexForKey("debugLoggingEnabled"));
|
||||
it("keeps large icon theme choices in appearance and debug logs in About", () => {
|
||||
expect(sourceIndexForKey("iconTheme")).toBeLessThan(sourceIndexForKey("debugLoggingEnabled"));
|
||||
expect(templateSource).toContain("data-icon-theme-settings");
|
||||
for (const key of ["iconThemeDefault", "iconThemeBlack"] as const) {
|
||||
const block = buttonBlockForKey(key);
|
||||
|
||||
Reference in new issue
Block a user