From cb4fd3a0e6d5d875035305526089c2df61fa808c Mon Sep 17 00:00:00 2001 From: Will Miao Date: Fri, 21 Aug 2026 09:14:52 +0800 Subject: [PATCH] refactor(ui): split settings modal into section templates with shared macros --- static/js/managers/SettingsManager.js | 3 + .../components/modals/settings/_macros.html | 69 + .../components/modals/settings/general.html | 326 ++++ .../components/modals/settings/interface.html | 127 ++ .../components/modals/settings/library.html | 482 ++++++ .../components/modals/settings_modal.html | 1495 +---------------- 6 files changed, 1019 insertions(+), 1483 deletions(-) create mode 100644 templates/components/modals/settings/_macros.html create mode 100644 templates/components/modals/settings/general.html create mode 100644 templates/components/modals/settings/interface.html create mode 100644 templates/components/modals/settings/library.html diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index 23f53bc9..5a196bed 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -904,6 +904,9 @@ export class SettingsManager { // Helper to update model Combobox presets from catalog / Ollama API const llmModelInput = document.getElementById('llmModel'); this._llmModelCombobox = null; + if (llmModelInput) { + llmModelInput.value = state.global.settings.llm_model || ''; + } if (llmModelInput && typeof Combobox !== 'undefined') { const currentProvider = llmProviderSelect ? llmProviderSelect.value : 'openai'; const fallbackModels = currentProvider === 'ollama' ? [] : (this._providerModels[currentProvider] || []); diff --git a/templates/components/modals/settings/_macros.html b/templates/components/modals/settings/_macros.html new file mode 100644 index 00000000..a2a5ffa4 --- /dev/null +++ b/templates/components/modals/settings/_macros.html @@ -0,0 +1,69 @@ +{# Shared building blocks for the settings modal sections. #} +{# Usage: {% import 'components/modals/settings/_macros.html' as sm with context %} #} +{# `with context` is required so macros can call the `t()` translation function. #} + +{% macro setting_toggle(id, key, label, help='') %} +
+
+
+ +
+
+ +
+
+
+{% endmacro %} + +{% macro setting_select(id, key, label, options, help='') %} +
+
+
+ +
+
+ +
+
+
+{% endmacro %} + +{% macro setting_input(id, key, label, placeholder, help='') %} +
+
+
+ +
+
+
+ +
+
+
+
+{% endmacro %} + +{% macro subsection_header(title) %} +
+

{{ t(title) }}

+
+{% endmacro %} diff --git a/templates/components/modals/settings/general.html b/templates/components/modals/settings/general.html new file mode 100644 index 00000000..8a79afb0 --- /dev/null +++ b/templates/components/modals/settings/general.html @@ -0,0 +1,326 @@ +{% import 'components/modals/settings/_macros.html' as sm with context %} + +
+ +
+
+
+ +
+
+ +
+
+
+ + + {{ sm.setting_toggle('usePortableSettings', 'use_portable_settings', 'settings.storage.locationLabel', 'settings.storage.locationHelp') }} + + +
+
+
+ + +
+
+ +
+ + + {{ t('settings.civitaiApiKeyNotConfigured') }} + + +
+ + +
+
+
+ + {{ sm.setting_select('civitaiHost', 'civitai_host', 'settings.civitaiHost.label', [ + ('civitai.com', 'settings.civitaiHost.options.com'), + ('civitai.red', 'settings.civitaiHost.options.red'), + ], 'settings.civitaiHost.help') }} + +
+ {{ sm.subsection_header('settings.sections.downloads') }} +
+
+
+ + + + + +
+
+ +
+
+
+ +
+ + +
+ {{ sm.subsection_header('settings.aiProvider.title') }} + {{ sm.setting_select('llmProvider', 'llm_provider', 'settings.aiProvider.provider', [ + ('openai', 'settings.aiProvider.providerOptions.openai'), + ('ollama', 'settings.aiProvider.providerOptions.ollama'), + ('deepseek', 'settings.aiProvider.providerOptions.deepseek'), + ('groq', 'settings.aiProvider.providerOptions.groq'), + ('openrouter', 'settings.aiProvider.providerOptions.openrouter'), + ('google', 'settings.aiProvider.providerOptions.google'), + ('opencode-go', 'settings.aiProvider.providerOptions.opencode-go'), + ('custom', 'settings.aiProvider.providerOptions.custom'), + ], 'settings.aiProvider.providerHelp') }} +
+
+
+ + +
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+ + + {{ t('settings.aiProvider.apiKeyNotSet') }} + + +
+ +
+
+
+
+
+
+ + +
+
+
+ +
+
+
+
+
+ + +
+ {{ sm.subsection_header('settings.sections.backup') }} +
+ {{ t('settings.backup.scopeHelp') }} +
+ {{ sm.setting_toggle('backupAutoEnabled', 'backup_auto_enabled', 'settings.backup.autoEnabled', 'settings.backup.autoEnabledHelp') }} +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+ +
+
+ + + +
+
+
+
+
+ {{ t('settings.backup.locationSummary') }} +
+ + +
+
+
+
+
+ +
+
+
+ + +
+ {{ sm.subsection_header('settings.sections.proxySettings') }} + {{ sm.setting_toggle('proxyEnabled', 'proxy_enabled', 'settings.proxySettings.enableProxy', 'settings.proxySettings.enableProxyHelp') }} + + +
+
diff --git a/templates/components/modals/settings/interface.html b/templates/components/modals/settings/interface.html new file mode 100644 index 00000000..a8477576 --- /dev/null +++ b/templates/components/modals/settings/interface.html @@ -0,0 +1,127 @@ +{% import 'components/modals/settings/_macros.html' as sm with context %} + +
+ +
+ {{ sm.subsection_header('settings.sections.contentFiltering') }} + {{ sm.setting_toggle('blurMatureContent', 'blur_mature_content', 'settings.contentFiltering.blurNsfwContent', 'settings.contentFiltering.blurNsfwContentHelp') }} + {{ sm.setting_toggle('showOnlySFW', 'show_only_sfw', 'settings.contentFiltering.showOnlySfw', 'settings.contentFiltering.showOnlySfwHelp') }} + {{ sm.setting_select('matureBlurLevel', 'mature_blur_level', 'settings.contentFiltering.matureBlurThreshold', [ + ('PG13', 'settings.contentFiltering.matureBlurThresholdOptions.pg13'), + ('R', 'settings.contentFiltering.matureBlurThresholdOptions.r'), + ('X', 'settings.contentFiltering.matureBlurThresholdOptions.x'), + ('XXX', 'settings.contentFiltering.matureBlurThresholdOptions.xxx'), + ], 'settings.contentFiltering.matureBlurThresholdHelp') }} +
+ + +
+ {{ sm.subsection_header('settings.sections.videoSettings') }} + {{ sm.setting_toggle('autoplayOnHover', 'autoplay_on_hover', 'settings.videoSettings.autoplayOnHover', 'settings.videoSettings.autoplayOnHoverHelp') }} +
+ + +
+ {{ sm.subsection_header('settings.sections.layoutSettings') }} +
+
+
+ +
+
+ +
+
+
    +
  • {{ t('settings.layoutSettings.displayDensityOptions.default') }}: {{ t('settings.layoutSettings.displayDensityDetails.default') }}
  • +
  • {{ t('settings.layoutSettings.displayDensityOptions.medium') }}: {{ t('settings.layoutSettings.displayDensityDetails.medium') }}
  • +
  • {{ t('settings.layoutSettings.displayDensityOptions.compact') }}: {{ t('settings.layoutSettings.displayDensityDetails.compact') }}
  • +
+ {{ t('settings.layoutSettings.displayDensityWarning') }} +
+
+ +
+
+
+ +
+
+
+ + +
+
+
+
+ + {{ sm.setting_select('modelNameDisplay', 'model_name_display', 'settings.layoutSettings.modelNameDisplay', [ + ('model_name', 'settings.layoutSettings.modelNameDisplayOptions.modelName'), + ('file_name', 'settings.layoutSettings.modelNameDisplayOptions.fileName'), + ], 'settings.layoutSettings.modelNameDisplayHelp') }} + + + {{ sm.setting_toggle('groupByModel', 'group_by_model', 'settings.layoutSettings.groupByModel', 'settings.layoutSettings.groupByModelHelp') }} + + {{ sm.setting_select('cardInfoDisplay', 'card_info_display', 'settings.layoutSettings.cardInfoDisplay', [ + ('always', 'settings.layoutSettings.cardInfoDisplayOptions.always'), + ('hover', 'settings.layoutSettings.cardInfoDisplayOptions.hover'), + ], 'settings.layoutSettings.cardInfoDisplayHelp') }} + +
+
+
+ +
+
+ + 8px +
+
+
+ + {{ sm.setting_toggle('showVersionOnCard', 'show_version_on_card', 'settings.layoutSettings.showVersionOnCard', 'settings.layoutSettings.showVersionOnCardHelp') }} + + {{ sm.setting_select('modelCardFooterAction', 'model_card_footer_action', 'settings.layoutSettings.modelCardFooterAction', [ + ('example_images', 'settings.layoutSettings.modelCardFooterActionOptions.exampleImages'), + ('replace_preview', 'settings.layoutSettings.modelCardFooterActionOptions.replacePreview'), + ], 'settings.layoutSettings.modelCardFooterActionHelp') }} +
+ + +
+ {{ sm.subsection_header('settings.sections.licenseIcons') }} + {{ sm.setting_toggle('useNewLicenseIcons', 'use_new_license_icons', 'settings.licenseIcons.useNewStyle', 'settings.licenseIcons.useNewStyleHelp') }} +
+ + +
+ {{ sm.subsection_header('settings.sections.misc') }} + {{ sm.setting_select('loraSyntaxFormat', 'lora_syntax_format', 'settings.misc.loraSyntaxFormat', [ + ('full', 'settings.misc.loraSyntaxFormatOptions.full'), + ('legacy', 'settings.misc.loraSyntaxFormatOptions.legacy'), + ], 'settings.misc.loraSyntaxFormatHelp') }} + {{ sm.setting_toggle('includeTriggerWords', 'include_trigger_words', 'settings.misc.includeTriggerWords', 'settings.misc.includeTriggerWordsHelp') }} +
+
diff --git a/templates/components/modals/settings/library.html b/templates/components/modals/settings/library.html new file mode 100644 index 00000000..a1522790 --- /dev/null +++ b/templates/components/modals/settings/library.html @@ -0,0 +1,482 @@ +{% import 'components/modals/settings/_macros.html' as sm with context %} +{% set template_preset_options = [ + ('', 'settings.downloadPathTemplates.templateOptions.flatStructure'), + ('{base_model}', 'settings.downloadPathTemplates.templateOptions.byBaseModel'), + ('{author}', 'settings.downloadPathTemplates.templateOptions.byAuthor'), + ('{first_tag}', 'settings.downloadPathTemplates.templateOptions.byFirstTag'), + ('{base_model}/{first_tag}', 'settings.downloadPathTemplates.templateOptions.baseModelFirstTag'), + ('{base_model}/{author}', 'settings.downloadPathTemplates.templateOptions.baseModelAuthor'), + ('{author}/{first_tag}', 'settings.downloadPathTemplates.templateOptions.authorFirstTag'), + ('{base_model}/{author}/{first_tag}', 'settings.downloadPathTemplates.templateOptions.baseModelAuthorFirstTag'), + ('custom', 'settings.downloadPathTemplates.templateOptions.customTemplate'), +] %} + +
+ +
+ {{ sm.subsection_header('settings.sections.folderSettings') }} +
+
+
+ +
+
+ +
+
+
+ + {{ sm.setting_select('defaultLoraRoot', 'default_lora_root', 'settings.folderSettings.defaultLoraRoot', [], 'settings.folderSettings.defaultLoraRootHelp') }} + + {{ sm.setting_select('defaultCheckpointRoot', 'default_checkpoint_root', 'settings.folderSettings.defaultCheckpointRoot', [], 'settings.folderSettings.defaultCheckpointRootHelp') }} + + {{ sm.setting_select('defaultUnetRoot', 'default_unet_root', 'settings.folderSettings.defaultUnetRoot', [], 'settings.folderSettings.defaultUnetRootHelp') }} + + {{ sm.setting_select('defaultEmbeddingRoot', 'default_embedding_root', 'settings.folderSettings.defaultEmbeddingRoot', [], 'settings.folderSettings.defaultEmbeddingRootHelp') }} +
+ + +
+ {{ sm.subsection_header('settings.sections.recipeSettings') }} + {{ sm.setting_input('recipesPath', 'recipes_path', 'settings.folderSettings.recipesPath', 'settings.folderSettings.recipesPathPlaceholder', 'settings.folderSettings.recipesPathHelp') }} +
+ + +
+
+

+ {{ t('settings.extraFolderPaths.title') }} + +

+
+
+
+ {{ t('settings.extraFolderPaths.description') }} +
+
+ + +
+
+
+ +
+
+ +
+
+
+
+
+ + +
+
+
+ +
+
+ +
+
+
+
+
+ + +
+
+
+ +
+
+ +
+
+
+
+
+ + +
+
+
+ +
+
+ +
+
+
+
+
+
+ + +
+
+

+ {{ t('settings.downloadPathTemplates.title') }} + +

+
+
+
+
+ {{ t('settings.downloadPathTemplates.availablePlaceholders') }} + {base_model} + {author} + {first_tag} + {model_name} + {version_name} +
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+
+
+
+
+
+ + {{ sm.setting_toggle('skipPreviouslyDownloadedModelVersions', 'skip_previously_downloaded_model_versions', 'settings.skipPreviouslyDownloadedModelVersions.label', 'settings.skipPreviouslyDownloadedModelVersions.help') }} + +
+
+
+ +
+
+ +
+
+ +
+
+ + +
+
+
+ + + + +
+
+
+ + + + +
+ + + +
+ +
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+
+ + +
+ {{ sm.subsection_header('settings.sections.versionScope') }} + {{ sm.setting_select('versionGrouping', 'version_grouping', 'settings.versionGrouping.label', [ + ('same_base', 'settings.versionGrouping.options.sameBase'), + ('any', 'settings.versionGrouping.options.any'), + ], 'settings.versionGrouping.help') }} + {{ sm.setting_toggle('hideEarlyAccessUpdates', 'hide_early_access_updates', 'settings.hideEarlyAccessUpdates.label', 'settings.hideEarlyAccessUpdates.help') }} + {{ sm.setting_toggle('hidePaidUpdates', 'hide_paid_updates', 'settings.hidePaidUpdates.label', 'settings.hidePaidUpdates.help') }} +
+ + +
+ {{ sm.subsection_header('settings.sections.exampleImages') }} +
+
+
+ +
+
+ + +
+
+
+ + {{ sm.setting_toggle('autoDownloadExampleImages', 'auto_download_example_images', 'settings.exampleImages.autoDownload', 'settings.exampleImages.autoDownloadHelp') }} + + {{ sm.setting_toggle('optimizeExampleImages', 'optimize_example_images', 'settings.exampleImages.optimizeImages', 'settings.exampleImages.optimizeImagesHelp') }} + +
+
+
+ +
+
+ +
+
+
+ + + + +
+ + +
+ {{ sm.subsection_header('settings.sections.autoOrganize') }} + + +
+
+
+ +
+
+ +
+
+
+ + +
+ {{ sm.subsection_header('settings.sections.metadata') }} + + +
+
+
+ +
+
+ +
+
+ + + {{ sm.setting_toggle('enableCivarchiveApi', 'enable_civarchive_api', 'settings.metadataArchive.enableCivarchiveApi', 'settings.metadataArchive.enableCivarchiveApiHelp') }} + + + {{ sm.setting_toggle('enableMetadataArchive', 'enable_metadata_archive_db', 'settings.metadataArchive.enableArchiveDb', 'settings.metadataArchive.enableArchiveDbHelp') }} + +
+ +
+ +
+
+
+ +
+
+ + +
+
+
+ + + {{ sm.setting_select('metadataProviderOrder', 'metadata_provider_order', 'settings.metadataArchive.providerOrder', [ + ('civitai_archive_sqlite', 'settings.metadataArchive.providerOrderCivitaiArchiveSqlite'), + ('civitai_sqlite_archive', 'settings.metadataArchive.providerOrderCivitaiSqliteArchive'), + ], 'settings.metadataArchive.providerOrderHelp') }} +
+
diff --git a/templates/components/modals/settings_modal.html b/templates/components/modals/settings_modal.html index 17a2c6a1..0cbab3df 100644 --- a/templates/components/modals/settings_modal.html +++ b/templates/components/modals/settings_modal.html @@ -39,1494 +39,23 @@ - +
- - -
- -
-
-
- -
-
- -
-
-
- - -
-
-
- -
-
- -
-
-
- - -
-
-
- - -
-
- -
- - - {{ t('settings.civitaiApiKeyNotConfigured') }} - - -
- - -
-
-
- -
-
-
- - -
-
- -
-
-
- -
-
-

{{ t('settings.sections.downloads') }}

-
-
-
-
- - - - - -
-
- -
-
-
- -
- - -
-
-

{{ t('settings.aiProvider.title') }}

-
-
-
-
- - -
-
- -
-
-
-
-
-
- - -
-
-
- -
-
-
-
-
-
-
- - -
-
-
- - - {{ t('settings.aiProvider.apiKeyNotSet') }} - - -
- -
-
-
-
-
-
- - -
-
-
- -
-
-
-
-
- - - - - - -
-
-

{{ t('settings.sections.backup') }}

-
-
- {{ t('settings.backup.scopeHelp') }} -
-
-
-
- -
-
- -
-
-
-
-
-
- -
-
-
- -
-
-
-
-
-
-
- -
-
- - - -
-
-
-
-
- {{ t('settings.backup.locationSummary') }} -
- - -
-
-
-
-
- -
-
-
- - -
-
-

{{ t('settings.sections.proxySettings') }}

-
-
-
-
- -
-
- -
-
-
- - -
-
- - -
- -
-
-

{{ t('settings.sections.contentFiltering') }}

-
-
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
- -
- - -
-
-

{{ t('settings.sections.videoSettings') }}

-
-
-
-
- -
-
- -
-
-
-
- - -
-
-

{{ t('settings.sections.layoutSettings') }}

-
-
-
-
- -
-
- -
-
-
    -
  • {{ t('settings.layoutSettings.displayDensityOptions.default') }}: {{ t('settings.layoutSettings.displayDensityDetails.default') }}
  • -
  • {{ t('settings.layoutSettings.displayDensityOptions.medium') }}: {{ t('settings.layoutSettings.displayDensityDetails.medium') }}
  • -
  • {{ t('settings.layoutSettings.displayDensityOptions.compact') }}: {{ t('settings.layoutSettings.displayDensityDetails.compact') }}
  • -
- {{ t('settings.layoutSettings.displayDensityWarning') }} -
-
- -
-
-
- -
-
-
- - -
-
-
-
- -
-
-
- -
-
- -
-
-
- - -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- - 8px -
-
-
- -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
-
- - -
-
-

{{ t('settings.sections.licenseIcons') }}

-
-
-
-
- -
-
- -
-
-
-
- - -
-
-

{{ t('settings.sections.misc') }}

-
-
-
-
- -
-
- -
-
-
-
-
-
- -
-
- -
-
-
-
- -
- - -
- -
-
-

{{ t('settings.sections.folderSettings') }}

-
-
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
- -
- - -
-
-

{{ t('settings.sections.recipeSettings') }}

-
-
-
-
- -
-
-
- -
-
-
-
-
- - -
-
-

- {{ t('settings.extraFolderPaths.title') }} - -

-
-
-
- {{ t('settings.extraFolderPaths.description') }} -
-
- - -
-
-
- -
-
- -
-
-
-
-
- - -
-
-
- -
-
- -
-
-
-
-
- - -
-
-
- -
-
- -
-
-
-
-
- - -
-
-
- -
-
- -
-
-
-
-
-
- - -
-
-

- {{ t('settings.downloadPathTemplates.title') }} - -

-
-
-
-
- {{ t('settings.downloadPathTemplates.availablePlaceholders') }} - {base_model} - {author} - {first_tag} - {model_name} - {version_name} -
-
-
- -
-
-
- -
-
- -
-
- -
-
- -
-
-
- -
-
- -
-
- -
-
- -
-
-
- -
-
- -
-
- -
-
- -
-
-
- -
-
- -
-
-
-
-
-
-
- -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
- -
-
- - -
-
-
- - - - -
-
-
- - - - -
- - - -
- -
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
- - -
-
-

{{ t('settings.sections.versionScope') }}

-
-
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
-
- - -
-
-

{{ t('settings.sections.exampleImages') }}

-
-
-
-
- -
-
- - -
-
-
- -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
- -
-
-
- -
-
- -
-
-
- - - - -
- - -
-
-

{{ t('settings.sections.autoOrganize') }}

-
- - -
-
-
- -
-
- -
-
-
- - -
-
-

{{ t('settings.sections.metadata') }}

-
- - -
-
-
- -
-
- -
-
- - -
-
-
- -
-
- -
-
-
- - -
-
-
- -
-
- -
-
-
- -
- -
- -
-
-
- -
-
- - -
-
-
- - -
-
-
- -
-
- -
-
-
-
-
+ {% include 'components/modals/settings/general.html' %} + {% include 'components/modals/settings/interface.html' %} + {% include 'components/modals/settings/library.html' %}
+ + + +