From 8155347fc1ec26340345509231436e75fd52989d Mon Sep 17 00:00:00 2001 From: Jacobinwwey Date: Sat, 13 Jun 2026 05:18:13 -0500 Subject: [PATCH 1/9] feat(db): add is_selected column and batch delete/update queries for models Add is_selected column to models table (default 1) to distinguish provider-supported models from user-selected models. Add deleteModels batch delete and updateModelsSelected batch update queries. New models fetched via refresh default to is_selected=0 (support area only). Signed-off-by: Jacob Signed-off-by: Jacobinwwey --- .../drizzle/0004_model_is_selected.sql | 1 + .../src/database/drizzle/meta/_journal.json | 7 ++++ apps/desktop/src/database/queries/models.ts | 32 ++++++++++++++++++- apps/desktop/src/database/schema.ts | 1 + apps/desktop/src/database/types/index.ts | 3 ++ 5 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 apps/desktop/src/database/drizzle/0004_model_is_selected.sql diff --git a/apps/desktop/src/database/drizzle/0004_model_is_selected.sql b/apps/desktop/src/database/drizzle/0004_model_is_selected.sql new file mode 100644 index 00000000..8a45ce78 --- /dev/null +++ b/apps/desktop/src/database/drizzle/0004_model_is_selected.sql @@ -0,0 +1 @@ +ALTER TABLE `models` ADD `is_selected` integer DEFAULT 1 NOT NULL; diff --git a/apps/desktop/src/database/drizzle/meta/_journal.json b/apps/desktop/src/database/drizzle/meta/_journal.json index 39fea45f..4641b6b3 100644 --- a/apps/desktop/src/database/drizzle/meta/_journal.json +++ b/apps/desktop/src/database/drizzle/meta/_journal.json @@ -29,6 +29,13 @@ "when": 1781021002699, "tag": "0003_tough_amazoness", "breakpoints": true + }, + { + "idx": 4, + "version": "6", + "when": 1781022000000, + "tag": "0004_model_is_selected", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/desktop/src/database/queries/models.ts b/apps/desktop/src/database/queries/models.ts index 0b2975cf..70728fd5 100644 --- a/apps/desktop/src/database/queries/models.ts +++ b/apps/desktop/src/database/queries/models.ts @@ -1,6 +1,6 @@ // Copyright (c) 2026. 千诚. Licensed under GPL v3 -import { and, desc, eq, sql } from 'drizzle-orm'; +import { and, desc, eq, inArray, sql } from 'drizzle-orm'; import { tt } from '@/i18n'; @@ -37,6 +37,7 @@ export const modelWithProviderSelection = { context_limit: models.context_limit, output_limit: models.output_limit, is_custom_metadata: models.is_custom_metadata, + is_selected: models.is_selected, provider_name: sql`${providers.name}`.as('provider_name'), provider_driver: sql`${providers.driver}`.as( 'provider_driver' @@ -187,6 +188,35 @@ export const deleteModel = async ({ id }: { id: number }): Promise => { return true; }; +/** + * 批量删除模型。 + */ +export const deleteModels = async ( + ids: number[], + database: DatabaseExecutor = db +): Promise => { + if (ids.length === 0) return; + + await database.delete(models).where(inArray(models.id, ids)).run(); +}; + +/** + * 批量更新模型选择状态。 + */ +export const updateModelsSelected = async ( + ids: number[], + isSelected: number, + database: DatabaseExecutor = db +): Promise => { + if (ids.length === 0) return; + + await database + .update(models) + .set({ is_selected: isSelected, updated_at: sql`(datetime('now'))` }) + .where(inArray(models.id, ids)) + .run(); +}; + /** * 根据 provider_id 和 model_id 查找模型(包含服务商信息)。 */ diff --git a/apps/desktop/src/database/schema.ts b/apps/desktop/src/database/schema.ts index be0a4250..c5c4012d 100644 --- a/apps/desktop/src/database/schema.ts +++ b/apps/desktop/src/database/schema.ts @@ -268,6 +268,7 @@ export const models = sqliteTable('models', { context_limit: integer('context_limit'), output_limit: integer('output_limit'), is_custom_metadata: integer('is_custom_metadata').notNull().default(0), // 用户是否自定义了元数据 + is_selected: integer('is_selected').notNull().default(1), // 是否在模型选择区域(1=已选择,0=仅在支持区域) created_at: text('created_at') .notNull() .default(sql`(datetime('now'))`), diff --git a/apps/desktop/src/database/types/index.ts b/apps/desktop/src/database/types/index.ts index 2018f501..3abdc36c 100644 --- a/apps/desktop/src/database/types/index.ts +++ b/apps/desktop/src/database/types/index.ts @@ -225,6 +225,7 @@ export interface ModelEntity { context_limit: number | null; output_limit: number | null; is_custom_metadata: number; + is_selected: number; created_at: string; updated_at: string; } @@ -246,6 +247,7 @@ export interface ModelCreateData { context_limit?: number | null; output_limit?: number | null; is_custom_metadata?: number; + is_selected?: number; created_at?: string; updated_at?: string; } @@ -280,6 +282,7 @@ export interface ModelWithProvider { context_limit: number | null; output_limit: number | null; is_custom_metadata: number; + is_selected: number; provider_name: string; provider_driver: DbProviderDriver; api_endpoint: string; From 1b3c70ec3fed13c6853e071d6d45f80879565f70 Mon Sep 17 00:00:00 2001 From: Jacobinwwey Date: Sat, 13 Jun 2026 05:18:23 -0500 Subject: [PATCH 2/9] feat(i18n): add keys for multi-select, regex search, and area labels Add zh-CN and en-US translation keys for: multi-select mode toggle, batch delete confirmation/success, regex search toggle and validation, model support/selection area titles and descriptions, add-to-selection and remove-from-selection actions. Signed-off-by: Jacob Signed-off-by: Jacobinwwey --- apps/desktop/src/i18n/messages.ts | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/apps/desktop/src/i18n/messages.ts b/apps/desktop/src/i18n/messages.ts index 1d1c41fb..3d79093d 100644 --- a/apps/desktop/src/i18n/messages.ts +++ b/apps/desktop/src/i18n/messages.ts @@ -434,6 +434,24 @@ const zhCNMessages = { 'settings.ai.noMatchingModels': '未找到匹配的模型', 'settings.ai.noMatchingModelsDescription': '没有找到与 "{query}" 匹配的模型,请尝试其他关键词', 'settings.ai.noModels': '暂无模型', + 'settings.ai.multiSelect': '多选', + 'settings.ai.multiSelectExit': '退出多选', + 'settings.ai.batchDelete': '删除所选', + 'settings.ai.confirmBatchDelete': '确定要删除所选的 {count} 个模型吗?', + 'settings.ai.batchDeleteSucceeded': '已删除 {count} 个模型', + 'settings.ai.batchDeleteContainsDefault': '所选模型包含默认模型,无法批量删除', + 'settings.ai.selectedCount': '已选 {count} 项', + 'settings.ai.selectNone': '取消全选', + 'settings.ai.selectAll': '全选', + 'settings.ai.regexSearch': '正则搜索', + 'settings.ai.regexSearchExit': '退出正则', + 'settings.ai.invalidRegex': '正则表达式无效:{error}', + 'settings.ai.modelSupportArea': '模型支持区域', + 'settings.ai.modelSupportAreaDescription': '以下为服务商支持的所有模型', + 'settings.ai.modelSelectionArea': '模型选择区域', + 'settings.ai.modelSelectionAreaDescription': '以下为您手动添加的模型', + 'settings.ai.addToSelection': '添加到选择区域', + 'settings.ai.removeFromSelection': '从选择区域移除', 'settings.ai.emptyModelsConfigureApi': '请先在上方配置 API 地址,然后点击下方按钮获取模型列表', 'settings.ai.emptyModelsFetchOrAdd': '点击下方按钮从服务商获取模型列表,或手动添加模型', 'settings.ai.fetchingModels': '获取中...', @@ -1393,6 +1411,26 @@ const enUSMessages: Record = { 'settings.ai.noMatchingModels': 'No matching models', 'settings.ai.noMatchingModelsDescription': 'No models matched "{query}". Try another keyword.', 'settings.ai.noModels': 'No models yet', + 'settings.ai.multiSelect': 'Multi-select', + 'settings.ai.multiSelectExit': 'Exit multi-select', + 'settings.ai.batchDelete': 'Delete selected', + 'settings.ai.confirmBatchDelete': + 'Delete {count} selected model? | Delete {count} selected models?', + 'settings.ai.batchDeleteSucceeded': 'Deleted {count} model | Deleted {count} models', + 'settings.ai.batchDeleteContainsDefault': + 'The selection contains the default model and cannot be deleted in bulk', + 'settings.ai.selectedCount': '{count} selected', + 'settings.ai.selectNone': 'Deselect all', + 'settings.ai.selectAll': 'Select all', + 'settings.ai.regexSearch': 'Regex search', + 'settings.ai.regexSearchExit': 'Exit regex', + 'settings.ai.invalidRegex': 'Invalid regex: {error}', + 'settings.ai.modelSupportArea': 'Model support area', + 'settings.ai.modelSupportAreaDescription': 'All models supported by this provider', + 'settings.ai.modelSelectionArea': 'Model selection area', + 'settings.ai.modelSelectionAreaDescription': 'Models you have manually added', + 'settings.ai.addToSelection': 'Add to selection', + 'settings.ai.removeFromSelection': 'Remove from selection', 'settings.ai.emptyModelsConfigureApi': 'Configure the API URL above, then fetch the model list below', 'settings.ai.emptyModelsFetchOrAdd': From 3506f1bc0990a341b918faaf18f8d4f9f73de301 Mon Sep 17 00:00:00 2001 From: Jacobinwwey Date: Sat, 13 Jun 2026 05:18:34 -0500 Subject: [PATCH 3/9] feat(settings): multi-select model deletion, regex search, and dual-area model list ModelList now splits into: - Model Selection Area: models the user has explicitly added (is_selected=1) - Model Support Area: all provider-supported models (is_selected=0) Features added: - Multi-select mode with checkbox selection, select-all, and batch delete - Regex search toggle (.* button in search bar) with error validation - Add-to-selection / remove-from-selection buttons on ModelCard - Batch delete with confirmation dialog blocks default model deletion ModelCard gains multiSelectMode/isSelected/area props and toggle-select, add-to-selection, remove-from-selection events. ModelGroup passes through multi-select state with group-level select/deselect. AiServices orchestrator wires batch-delete, add-to-selection, and remove-from-selection events to the new deleteModels and updateModelsSelected database queries. Signed-off-by: Jacob Signed-off-by: Jacobinwwey --- .../AiServices/components/ModelCard.vue | 63 +++- .../AiServices/components/ModelGroup.vue | 61 +++- .../AiServices/components/ModelList.vue | 306 ++++++++++++++++-- .../components/AiServices/index.vue | 62 +++- 4 files changed, 439 insertions(+), 53 deletions(-) diff --git a/apps/desktop/src/views/SettingsView/components/AiServices/components/ModelCard.vue b/apps/desktop/src/views/SettingsView/components/AiServices/components/ModelCard.vue index 66fd0ce5..ecc84336 100644 --- a/apps/desktop/src/views/SettingsView/components/AiServices/components/ModelCard.vue +++ b/apps/desktop/src/views/SettingsView/components/AiServices/components/ModelCard.vue @@ -1,4 +1,4 @@ - +