diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index ce321f1805..5e293ad07d 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -218,7 +218,18 @@ import { useSavedSqlStore } from "@/stores/savedSqlStore"; import { usePromptTemplateStore } from "@/stores/promptTemplateStore"; import { useTunnelProfileStore } from "@/stores/tunnelProfileStore"; import { currentLocale, previewLocale, restoreLocalePreview, setLocale, type Locale } from "@/i18n"; -import { SETTINGS_SEARCH_DEFINITIONS, TOOLBAR_VISIBILITY_ITEMS, createShortcutSettingsSearchDefinitions, resolveSettingsSearchEntries, searchSettings, toolbarVisibilityItemLabel, type SettingsCategory, type SettingsSearchEntry, type ToolbarVisibilityItem } from "@/lib/settings/settingsSearch"; +import { + SETTINGS_SEARCH_DEFINITIONS, + TOOLBAR_VISIBILITY_ITEMS, + createShortcutSettingsSearchDefinitions, + resolveSettingsCategory, + resolveSettingsSearchEntries, + searchSettings, + toolbarVisibilityItemLabel, + type SettingsCategory, + type SettingsSearchEntry, + type ToolbarVisibilityItem, +} from "@/lib/settings/settingsSearch"; import { LOCALE_OPTIONS } from "@/lib/app/localeOptions"; import { DEFAULT_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES, DEFAULT_WEB_DAV_REMOTE_PATH, normalizedWebDavAutoUploadInterval, writeWebDavAutoUploadFields } from "@/lib/webdav/webdavAutoUploadConfig"; import { apiUrl, webPath } from "@/lib/common/webPath"; @@ -1662,6 +1673,7 @@ function resetDefaultsForTab(tab: SettingsCategory) { editAppCloseUnsavedTabsMode.value = DEFAULT_EDITOR_SETTINGS.appCloseUnsavedTabsMode; editSavedSqlOpenTargetMode.value = DEFAULT_EDITOR_SETTINGS.savedSqlOpenTargetMode; editShowTableDdlHoverPreview.value = DEFAULT_EDITOR_SETTINGS.showTableDdlHoverPreview; + editExternalSqlEditorMaxMb.value = DEFAULT_EDITOR_SETTINGS.externalSqlEditorMaxMb; editClickTableNavigationTarget.value = DEFAULT_EDITOR_SETTINGS.clickTableNavigationTarget; editSqlVariableSubstitutionEnabled.value = DEFAULT_EDITOR_SETTINGS.sqlVariableSubstitutionEnabled; editSqlVariableSyntaxOverrides.value = normalizeSqlVariableSyntaxOverrides(DEFAULT_EDITOR_SETTINGS.sqlVariableSyntaxOverrides); @@ -1735,7 +1747,6 @@ function resetDefaultsForTab(tab: SettingsCategory) { editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize; editQueryResultMaxRowsEnabled.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRowsEnabled; editQueryResultMaxRows.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRows; - editExternalSqlEditorMaxMb.value = DEFAULT_EDITOR_SETTINGS.externalSqlEditorMaxMb; editInfiniteScroll.value = DEFAULT_EDITOR_SETTINGS.infiniteScroll; editRegexMaxMatchCount.value = DEFAULT_EDITOR_SETTINGS.regexMaxMatchCount; editAutoCalculateTotalRows.value = DEFAULT_EDITOR_SETTINGS.autoCalculateTotalRows; @@ -2211,7 +2222,6 @@ const settingsCategoryNav = computed<{ value: SettingsCategory; label: string }[ { value: "formatter", label: t("settings.sqlFormatterTab") }, { value: "navigation", label: t("settings.navigationTab") }, { value: "data", label: t("settings.dataTab") }, - { value: "sqlFile", label: t("settings.sqlFileSizeTab") }, ...(isWeb ? [] : [{ value: "backups" as const, label: t("databaseBackup.title") }]), { value: "tunnels", label: t("settings.tunnelsTab") }, { value: "shortcuts", label: t("settings.shortcutsTab") }, @@ -2222,7 +2232,7 @@ const settingsCategoryNav = computed<{ value: SettingsCategory; label: string }[ ...(isWeb ? [{ value: "security" as const, label: t("settings.securityTab") }] : []), { value: "about", label: t("settings.aboutTab") }, ]); -const settingsTabsWithApplyFooter = new Set(["editor", "formatter", "appearance", "navigation", "data", "sqlFile", "shortcuts", "snippets"]); +const settingsTabsWithApplyFooter = new Set(["editor", "formatter", "appearance", "navigation", "data", "shortcuts", "snippets"]); function hasSettingsApplyFooter(value: SettingsCategory): boolean { return settingsTabsWithApplyFooter.has(value); @@ -3525,7 +3535,7 @@ watch( mcpPolicyLoadError.value = ""; aiConfigListMode.value = "list"; aiEditConfigId.value = null; - activeSettingsTab.value = props.initialTab || "appearance"; + activeSettingsTab.value = resolveSettingsCategory(props.initialTab); passwordMessage.value = ""; oldPassword.value = ""; newPassword.value = ""; @@ -3595,7 +3605,7 @@ watch( () => props.initialTab, (tab) => { if (!settingsVisible.value || !tab) return; - activeSettingsTab.value = tab; + activeSettingsTab.value = resolveSettingsCategory(tab); void scrollToInitialSettingsSection(); }, ); @@ -3604,7 +3614,7 @@ watch( () => props.navigationRequestId, () => { if (!settingsVisible.value || !props.initialTab) return; - activeSettingsTab.value = props.initialTab; + activeSettingsTab.value = resolveSettingsCategory(props.initialTab); void scrollToInitialSettingsSection(); }, ); @@ -3650,7 +3660,7 @@ watch(activeSettingsTab, async (tab) => { await promptTemplateStore.ensureLoaded(); editGlobalInstructions.value = promptTemplateStore.globalInstructions; } - if (tab === "sqlFile" && isWeb) void loadWebSqlFileUploadMaxMbSetting(); + if (tab === "data" && isWeb) void loadWebSqlFileUploadMaxMbSetting(); if (tab === "about" && !appSupportInfo.value) void refreshAppSupportInfo(); if (tab === "appearance") { checkLayoutDescTruncation(); @@ -5586,6 +5596,34 @@ onUnmounted(() => { + + + +
+
+ {{ t("settings.sqlFileSection") }} +
+
+
+ +

+ {{ t("settings.externalSqlEditorMaxMbDescription", { min: MIN_EXTERNAL_SQL_EDITOR_FILE_MB, max: MAX_EXTERNAL_SQL_EDITOR_FILE_MB }) }} +

+
+ +
+
@@ -7165,6 +7203,40 @@ onUnmounted(() => { +
+
+ {{ t("settings.sqlFileSection") }} +
+
+
+ +

+ {{ t("settings.webSqlFileUploadMaxMbDescription", { min: MIN_EXTERNAL_SQL_EDITOR_FILE_MB, max: MAX_EXTERNAL_SQL_EDITOR_FILE_MB }) }} +

+
+
+ + +
+
+
+ + +
{{ t("settings.performanceSection") }} @@ -7292,55 +7364,6 @@ onUnmounted(() => {
-
-
-
- -

- {{ t("settings.externalSqlEditorMaxMbDescription", { min: MIN_EXTERNAL_SQL_EDITOR_FILE_MB, max: MAX_EXTERNAL_SQL_EDITOR_FILE_MB }) }} -

-
- -
-
-
- -

- {{ t("settings.webSqlFileUploadMaxMbDescription", { min: MIN_EXTERNAL_SQL_EDITOR_FILE_MB, max: MAX_EXTERNAL_SQL_EDITOR_FILE_MB }) }} -

-
-
- - -
-
-
-
diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index 7785104ee4..fcb3c2b4dc 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -6550,7 +6550,7 @@ export default { appearanceTab: "Appearance", navigationTab: "Navigation", dataTab: "Data", - sqlFileSizeTab: "SQL File Size", + sqlFileSection: "SQL Files", tunnelsTab: "Tunnels", tunnelsDescription: "Reusable SSH / proxy / HTTP tunnel configurations. Configure once, then select the profile from a connection's tunnel tab; edits here apply to every connection using the profile.", tunnelsEmpty: "No tunnel profiles yet. Create one here, then select it in a connection's tunnel settings.", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index fa19f8cadb..5f3b7aa99d 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -6141,7 +6141,7 @@ export default withEnglishFallback({ appearanceTab: "Apariencia", navigationTab: "Navegación", dataTab: "Datos", - sqlFileSizeTab: "Tamaño de archivo SQL", + sqlFileSection: "Archivos SQL", tunnelsTab: "Túneles", tunnelsDescription: "Configuraciones reutilizables de túneles SSH / proxy / HTTP. Configura una vez y selecciona el perfil desde la pestaña de túnel de una conexión; los cambios aquí se aplican a todas las conexiones que usan el perfil.", tunnelsEmpty: "Aún no hay perfiles de túnel. Crea uno aquí y selecciónalo en la configuración de túnel de una conexión.", diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index 3db84fbd4a..d142ecc886 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -6141,7 +6141,7 @@ export default withEnglishFallback({ appearanceTab: "Aspetto", navigationTab: "Navigazione", dataTab: "Dati", - sqlFileSizeTab: "Dimensione file SQL", + sqlFileSection: "File SQL", tunnelsTab: "Tunnel", tunnelsDescription: "Configurazioni riutilizzabili di tunnel SSH / proxy / HTTP. Configura una volta, poi seleziona il profilo dalla scheda tunnel di una connessione; le modifiche qui si applicano a tutte le connessioni che usano il profilo.", tunnelsEmpty: "Nessun profilo tunnel. Creane uno qui, poi selezionalo nelle impostazioni tunnel di una connessione.", diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts index 070483a2e5..d79789754b 100644 --- a/apps/desktop/src/i18n/locales/ja.ts +++ b/apps/desktop/src/i18n/locales/ja.ts @@ -6179,7 +6179,7 @@ export default withEnglishFallback({ appearanceTab: "外観", navigationTab: "ナビゲーション", dataTab: "データ", - sqlFileSizeTab: "SQLファイルサイズ", + sqlFileSection: "SQLファイル", tunnelsTab: "トンネル", tunnelsDescription: "再利用可能な SSH / プロキシ / HTTP トンネル設定です。一度設定すれば、接続のトンネルタブからプロファイルを選択するだけで使えます。ここでの編集は、このプロファイルを使用するすべての接続に反映されます。", tunnelsEmpty: "トンネルプロファイルがまだありません。ここで作成し、接続のトンネル設定で選択してください。", diff --git a/apps/desktop/src/i18n/locales/ko.ts b/apps/desktop/src/i18n/locales/ko.ts index 409420a31a..7940bc5cfc 100644 --- a/apps/desktop/src/i18n/locales/ko.ts +++ b/apps/desktop/src/i18n/locales/ko.ts @@ -5876,7 +5876,7 @@ export default withEnglishFallback({ appearanceTab: "모양", navigationTab: "탐색", dataTab: "데이터", - sqlFileSizeTab: "SQL 파일 크기", + sqlFileSection: "SQL 파일", tunnelsTab: "터널", tunnelsDescription: "재사용 가능한 SSH/프록시/HTTP 터널 설정입니다. 한 번 구성한 후 연결의 터널 탭에서 프로필을 선택하세요. 여기서의 편집은 이 프로필을 사용하는 모든 연결에 적용됩니다.", tunnelsEmpty: "아직 터널 프로필이 없습니다. 여기서 만든 후 연결의 터널 설정에서 선택하세요.", diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index 36c03a7e86..c916c40140 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -6143,7 +6143,7 @@ export default withEnglishFallback({ appearanceTab: "Aparência", navigationTab: "Navegação", dataTab: "Dados", - sqlFileSizeTab: "Tamanho de arquivo SQL", + sqlFileSection: "Arquivos SQL", tunnelsTab: "Túneis", tunnelsDescription: "Configurações reutilizáveis de túneis SSH / proxy / HTTP. Configure uma vez e selecione o perfil na aba de túnel de uma conexão; as edições aqui se aplicam a todas as conexões que usam o perfil.", tunnelsEmpty: "Ainda não há perfis de túnel. Crie um aqui e selecione-o nas configurações de túnel de uma conexão.", diff --git a/apps/desktop/src/i18n/locales/tr.ts b/apps/desktop/src/i18n/locales/tr.ts index 4ff32c21e4..d5a23d96a1 100644 --- a/apps/desktop/src/i18n/locales/tr.ts +++ b/apps/desktop/src/i18n/locales/tr.ts @@ -6436,7 +6436,7 @@ export default withEnglishFallback({ appearanceTab: "Görünüm", navigationTab: "Gezinme", dataTab: "Veri", - sqlFileSizeTab: "SQL Dosya Boyutu", + sqlFileSection: "SQL Dosyaları", tunnelsTab: "Tüneller", tunnelsDescription: "Yeniden kullanılabilir SSH / proxy / HTTP tünel yapılandırmaları. Bir kez yapılandırın, sonra bağlantının tünel sekmesinden profili seçin; buradaki değişiklikler profili kullanan her bağlantıda geçerli olur.", tunnelsEmpty: "Henüz tünel profili yok. Burada bir tane oluşturun, ardından bir bağlantının tünel ayarlarından seçin.", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index 4689bcb7d4..ed43acde78 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -6533,7 +6533,7 @@ export default withEnglishFallback({ appearanceTab: "外观", navigationTab: "导航", dataTab: "数据", - sqlFileSizeTab: "SQL 文件大小", + sqlFileSection: "SQL 文件", tunnelsTab: "隧道维护", tunnelsDescription: "可复用的 SSH / 代理 / HTTP 隧道配置。一次配置,之后在连接的隧道页签中选择档案即可;此处的修改会对所有使用该档案的连接生效。", tunnelsEmpty: "还没有隧道档案。先在这里创建,然后在连接的隧道设置中选择使用。", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index 121c5244d2..b9aefeeb13 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -5469,7 +5469,7 @@ export default withEnglishFallback({ securityTab: "安全", aboutTab: "關於我們", dataTab: "資料", - sqlFileSizeTab: "SQL 檔案大小", + sqlFileSection: "SQL 檔案", tunnelsTab: "隧道維護", tunnelsDescription: "可重複使用的 SSH / 代理 / HTTP 隧道設定。一次設定,之後在連線的隧道頁籤中選擇設定檔即可;此處的變更會套用到所有使用該設定檔的連線。", tunnelsEmpty: "還沒有隧道設定檔。先在這裡建立,然後在連線的隧道設定中選擇使用。", diff --git a/apps/desktop/src/lib/settings/__tests__/settingsSearch.spec.ts b/apps/desktop/src/lib/settings/__tests__/settingsSearch.spec.ts index 58f4d42cd4..011638d71c 100644 --- a/apps/desktop/src/lib/settings/__tests__/settingsSearch.spec.ts +++ b/apps/desktop/src/lib/settings/__tests__/settingsSearch.spec.ts @@ -1,6 +1,16 @@ import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; -import { SETTINGS_SEARCH_DEFINITIONS, TOOLBAR_VISIBILITY_ITEMS, createShortcutSettingsSearchDefinitions, createToolbarVisibilitySettingsSearchDefinitions, resolveSettingsSearchEntries, searchSettings, type SettingsCategory, type SettingsSearchDefinition } from "@/lib/settings/settingsSearch"; +import { + SETTINGS_SEARCH_DEFINITIONS, + TOOLBAR_VISIBILITY_ITEMS, + createShortcutSettingsSearchDefinitions, + createToolbarVisibilitySettingsSearchDefinitions, + resolveSettingsCategory, + resolveSettingsSearchEntries, + searchSettings, + type SettingsCategory, + type SettingsSearchDefinition, +} from "@/lib/settings/settingsSearch"; const settingsDialogSource = readFileSync(new URL("../../../components/editor/EditorSettingsDialog.vue", import.meta.url), "utf8"); @@ -78,6 +88,41 @@ describe("settings search", () => { expect(settingsDialogSource).toContain('v-model="editMultiStatementDefaultView"'); }); + it("places SQL file limits in their owning settings categories", () => { + expect(SETTINGS_SEARCH_DEFINITIONS).toContainEqual({ + id: "sql-file-editor-max-mb", + category: "editor", + titleKey: "settings.externalSqlEditorMaxMb", + descriptionKey: "settings.externalSqlEditorMaxMbDescription", + targetId: "editor-sql-file", + }); + expect(SETTINGS_SEARCH_DEFINITIONS).toContainEqual({ + id: "sql-file-web-upload-max-mb", + category: "data", + titleKey: "settings.webSqlFileUploadMaxMb", + descriptionKey: "settings.webSqlFileUploadMaxMbDescription", + targetId: "data-sql-file-upload", + visible: expect.any(Function), + }); + + const desktopEntries = resolveSettingsSearchEntries(SETTINGS_SEARCH_DEFINITIONS, { isWeb: false, visibleCategories: new Set(["editor", "data"]) }, translate, categoryLabels); + const webEntries = resolveSettingsSearchEntries(SETTINGS_SEARCH_DEFINITIONS, { isWeb: true, visibleCategories: new Set(["editor", "data"]) }, translate, categoryLabels); + expect(desktopEntries.map((entry) => entry.id)).toContain("sql-file-editor-max-mb"); + expect(desktopEntries.map((entry) => entry.id)).not.toContain("sql-file-web-upload-max-mb"); + expect(webEntries.map((entry) => entry.id)).toEqual(expect.arrayContaining(["sql-file-editor-max-mb", "sql-file-web-upload-max-mb"])); + expect(settingsDialogSource).toContain('data-settings-search-id="editor-sql-file"'); + expect(settingsDialogSource).toContain('data-settings-search-id="data-sql-file-upload"'); + }); + + it("maps legacy SQL file settings navigation to the editor", () => { + expect(resolveSettingsCategory("sqlFile")).toBe("editor"); + expect(resolveSettingsCategory()).toBe("appearance"); + expect(resolveSettingsCategory("removed-category")).toBe("appearance"); + expect(settingsDialogSource).not.toContain('value: "sqlFile"'); + expect(settingsDialogSource).not.toContain("activeSettingsTab === 'sqlFile'"); + expect(settingsDialogSource).toMatch(/if \(tab === "data" && isWeb\) void loadWebSqlFileUploadMaxMbSetting\(\)/); + }); + it("matches translated title, description, and category without changing declared order", () => { const entries = resolveSettingsSearchEntries(definitions, { isWeb: false, visibleCategories: allCategories }, translate, categoryLabels); expect(searchSettings(entries, "TYPEFACE", "en").map((entry) => entry.id)).toEqual(["font"]); diff --git a/apps/desktop/src/lib/settings/settingsSearch.ts b/apps/desktop/src/lib/settings/settingsSearch.ts index 1f42435563..d35423ab98 100644 --- a/apps/desktop/src/lib/settings/settingsSearch.ts +++ b/apps/desktop/src/lib/settings/settingsSearch.ts @@ -1,4 +1,15 @@ -export type SettingsCategory = "editor" | "formatter" | "appearance" | "navigation" | "data" | "sqlFile" | "backups" | "tunnels" | "shortcuts" | "snippets" | "sync" | "ai" | "mcp" | "security" | "about"; +export type SettingsCategory = "editor" | "formatter" | "appearance" | "navigation" | "data" | "backups" | "tunnels" | "shortcuts" | "snippets" | "sync" | "ai" | "mcp" | "security" | "about"; + +const SETTINGS_CATEGORIES: readonly SettingsCategory[] = ["editor", "formatter", "appearance", "navigation", "data", "backups", "tunnels", "shortcuts", "snippets", "sync", "ai", "mcp", "security", "about"]; + +/** + * Maps retired settings tabs to their current home so saved links and external + * navigation requests continue to open a populated settings page. + */ +export function resolveSettingsCategory(initialTab?: string): SettingsCategory { + if (initialTab === "sqlFile") return "editor"; + return SETTINGS_CATEGORIES.includes(initialTab as SettingsCategory) ? (initialTab as SettingsCategory) : "appearance"; +} export interface SettingsSearchContext { isWeb: boolean; @@ -199,8 +210,8 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] = { id: "data-grid-quick-entry", category: "data", titleKey: "settings.dataGridQuickEntry", descriptionKey: "settings.dataGridQuickEntryDescription", targetId: "data" }, { id: "data-grid-filter-view", category: "data", titleKey: "settings.dataGridFilterView", descriptionKey: "settings.dataGridFilterViewDescription", targetId: "data-grid-filter-view" }, { id: "data-grid-flattening-multi-line", category: "data", titleKey: "settings.flatteningMultiLineText", descriptionKey: "settings.flatteningMultiLineTextDescription", targetId: "data" }, - { id: "sql-file-editor-max-mb", category: "sqlFile", titleKey: "settings.externalSqlEditorMaxMb", descriptionKey: "settings.externalSqlEditorMaxMbDescription", targetId: "sqlFile" }, - { id: "sql-file-web-upload-max-mb", category: "sqlFile", titleKey: "settings.webSqlFileUploadMaxMb", descriptionKey: "settings.webSqlFileUploadMaxMbDescription", targetId: "sqlFile", visible: (context) => context.isWeb }, + { id: "sql-file-editor-max-mb", category: "editor", titleKey: "settings.externalSqlEditorMaxMb", descriptionKey: "settings.externalSqlEditorMaxMbDescription", targetId: "editor-sql-file" }, + { id: "sql-file-web-upload-max-mb", category: "data", titleKey: "settings.webSqlFileUploadMaxMb", descriptionKey: "settings.webSqlFileUploadMaxMbDescription", targetId: "data-sql-file-upload", visible: (context) => context.isWeb }, { id: "appearance-toolbar", category: "appearance", titleKey: "settings.toolbarTitle", descriptionKey: "settings.toolbarHiddenHint", targetId: "appearance" }, { id: "appearance-exclusive-sidebar-panels", category: "appearance", titleKey: "settings.exclusiveRightSidebarPanels", descriptionKey: "settings.exclusiveRightSidebarPanelsDescription", targetId: "appearance" }, ...createToolbarVisibilitySettingsSearchDefinitions(),