| | |
| | | |
| | | {#if settingsLoaded} |
| | | <div class="section"> |
| | | <div class="setting-row"> |
| | | <div class="setting-info"> |
| | | <span class="setting-label">{t('settings.language')}</span> |
| | | <span class="setting-desc"> |
| | | {languageModeVal === 'auto' |
| | | ? t('settings.languageAutoDesc') |
| | | : t('settings.languageManualDesc', { language: selectedLanguageName() })} |
| | | </span> |
| | | </div> |
| | | <div class="segmented language" role="group" aria-label={t('settings.language')}> |
| | | <div class="section-head"> |
| | | <span class="setting-label">{t('settings.language')}</span> |
| | | <span class="setting-desc"> |
| | | {languageModeVal === 'auto' |
| | | ? t('settings.languageAutoDesc') |
| | | : t('settings.languageManualDesc', { language: selectedLanguageName() })} |
| | | </span> |
| | | </div> |
| | | |
| | | <div class="language-grid" role="group" aria-label={t('settings.language')}> |
| | | <button |
| | | class:active={languageModeVal === 'auto'} |
| | | onclick={() => onLanguageChange('auto')} |
| | | > |
| | | {t('settings.languageAuto')} |
| | | </button> |
| | | {#each languageOptions as option} |
| | | <button |
| | | class:active={languageModeVal === 'auto'} |
| | | onclick={() => onLanguageChange('auto')} |
| | | class:active={languageModeVal === 'manual' && languageIDVal === option.id} |
| | | onclick={() => onLanguageChange(option.id)} |
| | | > |
| | | {t('settings.languageAuto')} |
| | | {option.nativeName || option.displayName} |
| | | </button> |
| | | {#each languageOptions as option} |
| | | <button |
| | | class:active={languageModeVal === 'manual' && languageIDVal === option.id} |
| | | onclick={() => onLanguageChange(option.id)} |
| | | > |
| | | {option.nativeName || option.displayName} |
| | | </button> |
| | | {/each} |
| | | </div> |
| | | {/each} |
| | | </div> |
| | | </div> |
| | | {/if} |
| | | |
| | | {#if settingsLoaded && modelOptions.length > 0} |
| | | <div class="section"> |
| | | <div class="setting-row models-header"> |
| | | <div class="setting-info"> |
| | | <span class="setting-label">{t('settings.recognitionModel')}</span> |
| | | <span class="setting-desc"> |
| | | {t('settings.recognitionModelDesc', { language: selectedLanguageName() })} |
| | | </span> |
| | | </div> |
| | | <div class="section-head"> |
| | | <span class="setting-label">{t('settings.recognitionModel')}</span> |
| | | <span class="setting-desc"> |
| | | {t('settings.recognitionModelDesc', { language: selectedLanguageName() })} |
| | | </span> |
| | | </div> |
| | | |
| | | <div class="model-list"> |
| | |
| | | {/if} |
| | | </span> |
| | | </div> |
| | | <button |
| | | class="model-action" |
| | | class:primary={!option.isCurrent} |
| | | disabled={option.isCurrent || !!modelBusyID} |
| | | onclick={() => onModelAction(option)} |
| | | > |
| | | {modelActionLabel(option)} |
| | | </button> |
| | | {#if option.isCurrent} |
| | | <span class="model-current-status">{t('settings.modelCurrent')}</span> |
| | | {:else} |
| | | <button |
| | | class="model-action primary" |
| | | disabled={!!modelBusyID} |
| | | onclick={() => onModelAction(option)} |
| | | > |
| | | {modelActionLabel(option)} |
| | | </button> |
| | | {/if} |
| | | </div> |
| | | {/each} |
| | | </div> |
| | |
| | | </div> |
| | | |
| | | <style> |
| | | .page { |
| | | width: 100%; |
| | | max-width: 920px; |
| | | } |
| | | |
| | | .header { |
| | | margin-bottom: var(--spacing-xl); |
| | | } |
| | |
| | | .section { |
| | | background: var(--color-bg-grouped-secondary); |
| | | border-radius: var(--radius-md); |
| | | padding: var(--spacing-lg); |
| | | padding: 18px 20px; |
| | | margin-bottom: var(--spacing-md); |
| | | } |
| | | |
| | | .setting-row { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | gap: var(--spacing-md); |
| | | padding: var(--spacing-xs) 0; |
| | | } |
| | | |
| | | .setting-info { |
| | | .section-head { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 2px; |
| | | min-width: 0; |
| | | gap: 4px; |
| | | margin-bottom: 14px; |
| | | } |
| | | |
| | | .setting-label { |
| | |
| | | |
| | | .setting-desc { |
| | | font-size: var(--font-size-xs); |
| | | color: var(--color-tertiary-label); |
| | | line-height: 1.35; |
| | | color: var(--color-secondary-label); |
| | | max-width: 560px; |
| | | } |
| | | |
| | | .segmented { |
| | | display: inline-grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | min-width: 148px; |
| | | padding: 2px; |
| | | border-radius: var(--radius-sm); |
| | | .language-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(4, minmax(0, 1fr)); |
| | | gap: 6px; |
| | | padding: 6px; |
| | | border-radius: var(--radius-md); |
| | | background: var(--color-bg-secondary); |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .segmented.language { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | justify-content: flex-end; |
| | | max-width: 390px; |
| | | min-width: 246px; |
| | | } |
| | | |
| | | .segmented.language button { |
| | | min-width: 76px; |
| | | .language-grid button { |
| | | height: 34px; |
| | | min-width: 0; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .segmented button { |
| | | min-width: 66px; |
| | | height: 30px; |
| | | padding: 0 12px; |
| | | border: none; |
| | | border-radius: calc(var(--radius-sm) - 2px); |
| | | border-radius: var(--radius-sm); |
| | | background: transparent; |
| | | color: var(--color-secondary-label); |
| | | font-size: var(--font-size-sm); |
| | | font-weight: 500; |
| | | cursor: pointer; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | transition: background var(--transition-fast), color var(--transition-fast); |
| | | } |
| | | |
| | | .segmented button.active { |
| | | .language-grid button.active { |
| | | background: var(--color-blue); |
| | | color: white; |
| | | } |
| | | |
| | | .models-header { |
| | | align-items: flex-start; |
| | | margin-bottom: var(--spacing-sm); |
| | | } |
| | | |
| | | .model-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | gap: 0; |
| | | overflow: hidden; |
| | | border: 1px solid var(--color-separator); |
| | | border-radius: var(--radius-md); |
| | | background: var(--color-bg-tertiary); |
| | | } |
| | | |
| | | .model-card { |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | gap: var(--spacing-md); |
| | | min-height: 78px; |
| | | padding: 10px 12px; |
| | | border: 1px solid var(--color-separator); |
| | | border-radius: var(--radius-sm); |
| | | background: var(--color-bg-secondary); |
| | | min-height: 72px; |
| | | padding: 12px 14px; |
| | | border: 0; |
| | | border-radius: 0; |
| | | background: var(--color-bg-tertiary); |
| | | } |
| | | |
| | | .model-card + .model-card { |
| | | border-top: 1px solid var(--color-separator); |
| | | } |
| | | |
| | | .model-card.current { |
| | | border-color: rgba(0, 122, 255, 0.38); |
| | | background: rgba(0, 122, 255, 0.055); |
| | | } |
| | | |
| | | .model-card.current::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 12px; |
| | | bottom: 12px; |
| | | width: 3px; |
| | | border-radius: 2px; |
| | | background: var(--color-blue); |
| | | } |
| | | |
| | | .model-main { |
| | |
| | | .model-title { |
| | | font-size: var(--font-size-base); |
| | | font-weight: 600; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .model-pill { |
| | |
| | | .model-desc, |
| | | .model-meta { |
| | | font-size: var(--font-size-xs); |
| | | line-height: 1.35; |
| | | color: var(--color-secondary-label); |
| | | } |
| | | |
| | | .model-meta { |
| | | color: var(--color-tertiary-label); |
| | | } |
| | | |
| | |
| | | opacity: 0.72; |
| | | } |
| | | |
| | | .model-current-status { |
| | | min-width: 64px; |
| | | text-align: center; |
| | | color: var(--color-secondary-label); |
| | | font-size: var(--font-size-sm); |
| | | font-weight: 500; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .model-error { |
| | | margin-top: 8px; |
| | | font-size: var(--font-size-xs); |
| | | color: var(--color-red); |
| | | } |
| | | |
| | | @media (max-width: 860px) { |
| | | .language-grid { |
| | | grid-template-columns: repeat(3, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 640px) { |
| | | .language-grid { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | |
| | | .model-card { |
| | | align-items: flex-start; |
| | | } |
| | | } |
| | | </style> |