feat(stage-ui): optimize card many select component layout styles (#1096)
--------- Co-authored-by: Rin <shinohara-rin@users.noreply.github.com>
This commit is contained in:
@@ -296,6 +296,7 @@ pages:
|
||||
provider-model-selection:
|
||||
collapse: Collapse
|
||||
custom_model_placeholder: Enter custom model name...
|
||||
current_model_label: 'Current model:'
|
||||
description: Select the suitable LLM provider for consciousness
|
||||
error: Error loading models
|
||||
health_check_failed: Health check failed
|
||||
|
||||
@@ -284,6 +284,7 @@ pages:
|
||||
provider-model-selection:
|
||||
collapse: Colapsar
|
||||
custom_model_placeholder: Ingresa el nombre del modelo personalizado...
|
||||
current_model_label: 'Modelo actual seleccionado:'
|
||||
description: Selecciona el proveedor LLM adecuado para la consciencia
|
||||
error: Error cargando modelos
|
||||
health_check_failed: Health check failed
|
||||
|
||||
@@ -284,6 +284,7 @@ pages:
|
||||
provider-model-selection:
|
||||
collapse: Réduire
|
||||
custom_model_placeholder: Entrez le nom du modèle personnalisé...
|
||||
current_model_label: 'Modèle actuel sélectionné:'
|
||||
description: Sélectionnez le fournisseur LLM approprié pour la conscience
|
||||
error: Erreur lors du chargement des modèles
|
||||
health_check_failed: Échec de la vérification de l'état de santé
|
||||
|
||||
@@ -284,6 +284,7 @@ pages:
|
||||
provider-model-selection:
|
||||
collapse: 折りたたむ
|
||||
custom_model_placeholder: カスタムモデル名を入力…
|
||||
current_model_label: '現在選択中のモデル:'
|
||||
description: 意識に適したLLMプロバイダーを選択
|
||||
error: モデルの読み込みエラー
|
||||
health_check_failed: ヘルスチェックに失敗しました
|
||||
|
||||
@@ -284,6 +284,7 @@ pages:
|
||||
provider-model-selection:
|
||||
collapse: 접기
|
||||
custom_model_placeholder: 사용자 지정 모델 이름 입력...
|
||||
current_model_label: '현재 선택된 모델:'
|
||||
description: 의식에 적합한 LLM 제공자를 선택합니다.
|
||||
error: 모델 목록을 불러오는데 오류가 발생했습니다.
|
||||
health_check_failed: 상태 점검 실패
|
||||
|
||||
@@ -284,6 +284,7 @@ pages:
|
||||
provider-model-selection:
|
||||
collapse: Свернуть
|
||||
custom_model_placeholder: Введите название кастомной модели...
|
||||
current_model_label: 'Текущая выбранная модель:'
|
||||
description: Выберите подходящего LLM-провайдера для сознания
|
||||
error: Ошибка загрузки моделей
|
||||
health_check_failed: Health check failed
|
||||
|
||||
@@ -284,6 +284,7 @@ pages:
|
||||
provider-model-selection:
|
||||
collapse: Thu gọn
|
||||
custom_model_placeholder: Nhập tên mô hình tùy chỉnh...
|
||||
current_model_label: 'Mô hình hiện được chọn:'
|
||||
description: Chọn nhà cung cấp LLM phù hợp cho ý thức
|
||||
error: Lỗi tải mô hình
|
||||
health_check_failed: Health check failed
|
||||
|
||||
@@ -284,6 +284,7 @@ pages:
|
||||
provider-model-selection:
|
||||
collapse: 折叠
|
||||
custom_model_placeholder: 输入自订模型名称
|
||||
current_model_label: '当前选择模型:'
|
||||
description: 为意识选择合适的 LLM 服务来源
|
||||
error: 获取出错啦
|
||||
health_check_failed: 健康检查失败
|
||||
|
||||
@@ -284,6 +284,7 @@ pages:
|
||||
provider-model-selection:
|
||||
collapse: 摺疊
|
||||
custom_model_placeholder: 輸入自訂模型名稱
|
||||
current_model_label: '目前選擇模型:'
|
||||
description: 為意識選擇合適的 LLM 服務來源
|
||||
error: 取得發生錯誤
|
||||
health_check_failed: 健康度檢查失敗
|
||||
|
||||
@@ -145,8 +145,9 @@ function handleDeleteProvider(providerId: string) {
|
||||
<h2 class="text-lg md:text-2xl">
|
||||
{{ t('settings.pages.modules.consciousness.sections.section.provider-model-selection.title') }}
|
||||
</h2>
|
||||
<div text="neutral-400 dark:neutral-400">
|
||||
<div class="flex flex-col items-start gap-1 text-neutral-400 md:flex-row md:items-center md:justify-between dark:text-neutral-400">
|
||||
<span>{{ t('settings.pages.modules.consciousness.sections.section.provider-model-selection.subtitle') }}</span>
|
||||
<span v-if="activeModel" class="text-sm text-neutral-400 font-medium dark:text-neutral-400">{{ t('settings.pages.modules.consciousness.sections.section.provider-model-selection.current_model_label') }} {{ activeModel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -195,7 +196,7 @@ function handleDeleteProvider(providerId: string) {
|
||||
<RadioCardManySelect
|
||||
v-model="activeModel"
|
||||
v-model:search-query="modelSearchQuery"
|
||||
:items="providerModels.sort((a, b) => a.id === activeModel ? -1 : b.id === activeModel ? 1 : 0)"
|
||||
:items="providerModels"
|
||||
:searchable="true"
|
||||
:allow-custom="true"
|
||||
:search-placeholder="t('settings.pages.modules.consciousness.sections.section.provider-model-selection.search_placeholder')"
|
||||
|
||||
@@ -585,14 +585,14 @@ onUnmounted(() => {
|
||||
<h2 class="text-lg md:text-2xl">
|
||||
{{ t('settings.pages.modules.consciousness.sections.section.provider-model-selection.title') }}
|
||||
</h2>
|
||||
<div text="neutral-400 dark:neutral-400">
|
||||
<!-- Show different description based on whether provider supports model listing and has models -->
|
||||
<div class="flex flex-col items-start gap-1 text-neutral-400 md:flex-row md:items-center md:justify-between dark:text-neutral-400">
|
||||
<span v-if="supportsModelListing && providerModels.length > 0">
|
||||
{{ t('settings.pages.modules.consciousness.sections.section.provider-model-selection.subtitle') }}
|
||||
</span>
|
||||
<span v-else>
|
||||
Enter the transcription model to use (e.g., 'whisper-1', 'gpt-4o-transcribe')
|
||||
</span>
|
||||
<span v-if="activeTranscriptionModel" class="text-sm text-neutral-400 font-medium dark:text-neutral-400">{{ t('settings.pages.modules.consciousness.sections.section.provider-model-selection.current_model_label') }} {{ activeTranscriptionModel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -335,8 +335,9 @@ function handleDeleteProvider(providerId: string) {
|
||||
<h2 class="text-lg md:text-2xl">
|
||||
{{ t('settings.pages.modules.consciousness.sections.section.provider-model-selection.title') }}
|
||||
</h2>
|
||||
<div text="neutral-400 dark:neutral-400">
|
||||
<div class="flex flex-col items-start gap-1 text-neutral-400 md:flex-row md:items-center md:justify-between dark:text-neutral-400">
|
||||
<span>{{ t('settings.pages.modules.consciousness.sections.section.provider-model-selection.subtitle') }}</span>
|
||||
<span v-if="activeSpeechModel" class="text-sm text-neutral-400 font-medium dark:text-neutral-400">{{ t('settings.pages.modules.consciousness.sections.section.provider-model-selection.current_model_label') }} {{ activeSpeechModel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ interface Item {
|
||||
|
||||
interface Props {
|
||||
items: Item[]
|
||||
columns?: number
|
||||
searchable?: boolean
|
||||
searchPlaceholder?: string
|
||||
searchNoResultsTitle?: string
|
||||
@@ -29,6 +30,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
columns: 2,
|
||||
searchable: true,
|
||||
searchPlaceholder: 'Search...',
|
||||
searchNoResultsTitle: 'No results found',
|
||||
@@ -92,6 +94,10 @@ const filteredItems = computed(() => {
|
||||
return result
|
||||
})
|
||||
|
||||
const showExpandCollapseBtn = computed(() => {
|
||||
return filteredItems.value.length > props.columns
|
||||
})
|
||||
|
||||
function updateCustomValue(value: string) {
|
||||
customValue.value = value
|
||||
emit('update:customValue', value)
|
||||
@@ -138,9 +144,10 @@ function updateCustomValue(value: string) {
|
||||
<!-- Responsive grid container -->
|
||||
<div
|
||||
:class="[
|
||||
'grid gap-4 mb-2',
|
||||
isListExpanded
|
||||
? 'grid grid-cols-1 md:grid-cols-2 gap-4'
|
||||
: 'grid auto-cols-[min(300px,calc((100vw-5rem)/2))] grid-flow-col gap-4 overflow-x-auto scrollbar-none pb-2',
|
||||
? 'grid-cols-1 md:grid-cols-[repeat(var(--cols),minmax(0,1fr))] snap-y snap-proximity'
|
||||
: 'grid-flow-col auto-cols-[calc((100%-(var(--cols)-1)*1rem)/var(--cols))] overflow-x-auto scrollbar-none snap-x snap-proximity',
|
||||
...(props.listClass
|
||||
? (typeof props.listClass === 'string'
|
||||
? [props.listClass]
|
||||
@@ -152,7 +159,7 @@ function updateCustomValue(value: string) {
|
||||
),
|
||||
]"
|
||||
transition="all duration-200 ease-in-out"
|
||||
:style="isListExpanded ? '' : 'scroll-snap-type: x mandatory;'"
|
||||
:style="{ '--cols': props.columns }"
|
||||
>
|
||||
<RadioCardDetail
|
||||
v-for="item in filteredItems"
|
||||
@@ -169,13 +176,14 @@ function updateCustomValue(value: string) {
|
||||
:custom-input-value="customValue"
|
||||
:custom-input-placeholder="customInputPlaceholder"
|
||||
name="radio-card-detail-many-select"
|
||||
class="scroll-snap-align-start"
|
||||
class="snap-start"
|
||||
@update:custom-input-value="updateCustomValue($event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Expand/collapse handle -->
|
||||
<div
|
||||
v-if="showExpandCollapseBtn"
|
||||
bg="neutral-100 dark:[rgba(0,0,0,0.3)]"
|
||||
rounded-xl
|
||||
:class="[
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ref } from 'vue'
|
||||
import VoiceCardManySelect from './voice-card-many-select.vue'
|
||||
|
||||
const selectedVoiceId = ref('voice1')
|
||||
const customValue = ref('')
|
||||
const searchQuery = ref('')
|
||||
|
||||
// Sample voice data
|
||||
@@ -44,10 +43,6 @@ function handleVoiceIdUpdate(value: string) {
|
||||
selectedVoiceId.value = value
|
||||
}
|
||||
|
||||
function handleCustomValueUpdate(value: string) {
|
||||
customValue.value = value
|
||||
}
|
||||
|
||||
function handleSearchQueryUpdate(value: string) {
|
||||
searchQuery.value = value
|
||||
}
|
||||
@@ -66,9 +61,8 @@ function handleSearchQueryUpdate(value: string) {
|
||||
<Variant id="default" title="Default">
|
||||
<VoiceCardManySelect
|
||||
:voices="voices"
|
||||
:selected-voice-id="selectedVoiceId"
|
||||
@update:selected-voice-id="handleVoiceIdUpdate"
|
||||
@update:custom-value="handleCustomValueUpdate"
|
||||
:voice-id="selectedVoiceId"
|
||||
@update:voice-id="handleVoiceIdUpdate"
|
||||
@update:search-query="handleSearchQueryUpdate"
|
||||
/>
|
||||
</Variant>
|
||||
@@ -76,20 +70,28 @@ function handleSearchQueryUpdate(value: string) {
|
||||
<Variant id="no-search" title="Without Search">
|
||||
<VoiceCardManySelect
|
||||
:voices="voices"
|
||||
:selected-voice-id="selectedVoiceId"
|
||||
:voice-id="selectedVoiceId"
|
||||
:searchable="false"
|
||||
@update:selected-voice-id="handleVoiceIdUpdate"
|
||||
@update:custom-value="handleCustomValueUpdate"
|
||||
@update:voice-id="handleVoiceIdUpdate"
|
||||
/>
|
||||
</Variant>
|
||||
|
||||
<Variant id="no-visualizer" title="Without Visualizer">
|
||||
<VoiceCardManySelect
|
||||
:voices="voices"
|
||||
:selected-voice-id="selectedVoiceId"
|
||||
:voice-id="selectedVoiceId"
|
||||
:show-visualizer="false"
|
||||
@update:selected-voice-id="handleVoiceIdUpdate"
|
||||
@update:custom-value="handleCustomValueUpdate"
|
||||
@update:voice-id="handleVoiceIdUpdate"
|
||||
@update:search-query="handleSearchQueryUpdate"
|
||||
/>
|
||||
</Variant>
|
||||
|
||||
<Variant id="custom-columns" title="Custom Columns">
|
||||
<VoiceCardManySelect
|
||||
:voices="voices"
|
||||
:voice-id="selectedVoiceId"
|
||||
:columns="3"
|
||||
@update:voice-id="handleVoiceIdUpdate"
|
||||
@update:search-query="handleSearchQueryUpdate"
|
||||
/>
|
||||
</Variant>
|
||||
@@ -97,7 +99,7 @@ function handleSearchQueryUpdate(value: string) {
|
||||
<Variant id="custom-text" title="Custom Text">
|
||||
<VoiceCardManySelect
|
||||
:voices="voices"
|
||||
:selected-voice-id="selectedVoiceId"
|
||||
:voice-id="selectedVoiceId"
|
||||
search-placeholder="Find a voice..."
|
||||
search-no-results-title="No matching voices"
|
||||
search-no-results-description="Please try another search term"
|
||||
@@ -107,8 +109,7 @@ function handleSearchQueryUpdate(value: string) {
|
||||
collapse-button-text="Collapse list"
|
||||
play-button-text="Listen"
|
||||
pause-button-text="Stop"
|
||||
@update:selected-voice-id="handleVoiceIdUpdate"
|
||||
@update:custom-value="handleCustomValueUpdate"
|
||||
@update:voice-id="handleVoiceIdUpdate"
|
||||
@update:search-query="handleSearchQueryUpdate"
|
||||
/>
|
||||
</Variant>
|
||||
@@ -116,9 +117,8 @@ function handleSearchQueryUpdate(value: string) {
|
||||
<Variant id="empty" title="No Voices">
|
||||
<VoiceCardManySelect
|
||||
:voices="[]"
|
||||
:selected-voice-id="selectedVoiceId"
|
||||
@update:selected-voice-id="handleVoiceIdUpdate"
|
||||
@update:custom-value="handleCustomValueUpdate"
|
||||
:voice-id="selectedVoiceId"
|
||||
@update:voice-id="handleVoiceIdUpdate"
|
||||
@update:search-query="handleSearchQueryUpdate"
|
||||
/>
|
||||
</Variant>
|
||||
@@ -126,9 +126,8 @@ function handleSearchQueryUpdate(value: string) {
|
||||
<Variant id="many-voices" title="Many Voices">
|
||||
<VoiceCardManySelect
|
||||
:voices="[...voices, ...voices, ...voices]"
|
||||
:selected-voice-id="selectedVoiceId"
|
||||
@update:selected-voice-id="handleVoiceIdUpdate"
|
||||
@update:custom-value="handleCustomValueUpdate"
|
||||
:voice-id="selectedVoiceId"
|
||||
@update:voice-id="handleVoiceIdUpdate"
|
||||
@update:search-query="handleSearchQueryUpdate"
|
||||
/>
|
||||
</Variant>
|
||||
|
||||
@@ -29,6 +29,7 @@ interface Voice {
|
||||
|
||||
interface Props {
|
||||
voices: Voice[]
|
||||
columns?: number
|
||||
searchable?: boolean
|
||||
searchPlaceholder?: string
|
||||
searchNoResultsTitle?: string
|
||||
@@ -46,6 +47,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
columns: 2,
|
||||
searchable: true,
|
||||
searchPlaceholder: 'Search voices...',
|
||||
searchNoResultsTitle: 'No voices found',
|
||||
@@ -111,6 +113,10 @@ const filteredVoices = computed(() => {
|
||||
})
|
||||
})
|
||||
|
||||
const showExpandCollapseBtn = computed(() => {
|
||||
return filteredVoices.value.length > props.columns
|
||||
})
|
||||
|
||||
// Get preview URL from either field
|
||||
function getPreviewUrl(voice: Voice): string | undefined {
|
||||
return voice.previewURL || voice.preview_audio_url
|
||||
@@ -341,9 +347,10 @@ const customVoiceName = ref('')
|
||||
<!-- Responsive grid container -->
|
||||
<div
|
||||
:class="[
|
||||
'grid gap-4 mb-2',
|
||||
isListExpanded
|
||||
? 'grid grid-cols-1 md:grid-cols-2 gap-4'
|
||||
: 'grid auto-cols-[min(300px,calc((100vw-5rem)/2))] grid-flow-col gap-4 overflow-x-auto scrollbar-none pb-2',
|
||||
? 'grid-cols-1 md:grid-cols-[repeat(var(--cols),minmax(0,1fr))] snap-y snap-proximity'
|
||||
: 'grid-flow-col auto-cols-[calc((100%-(var(--cols)-1)*1rem)/var(--cols))] overflow-x-auto scrollbar-none snap-x snap-proximity',
|
||||
...(props.listClass
|
||||
? (typeof props.listClass === 'string'
|
||||
? [props.listClass]
|
||||
@@ -355,7 +362,7 @@ const customVoiceName = ref('')
|
||||
),
|
||||
]"
|
||||
transition="all duration-200 ease-in-out"
|
||||
:style="isListExpanded ? '' : 'scroll-snap-type: x mandatory;'"
|
||||
:style="{ '--cols': props.columns }"
|
||||
>
|
||||
<!-- Not support voices warning -->
|
||||
<Alert v-if="!searchQuery && filteredVoices.length === 0" type="warning">
|
||||
@@ -374,6 +381,7 @@ const customVoiceName = ref('')
|
||||
v-model:voice-id="voiceId"
|
||||
v-model:custom-voice-name="customVoiceName"
|
||||
name="voice"
|
||||
class="snap-start"
|
||||
:voice="voice"
|
||||
:currently-playing-id="currentlyPlayingId"
|
||||
:custom-input-placeholder="customInputPlaceholder"
|
||||
@@ -385,6 +393,7 @@ const customVoiceName = ref('')
|
||||
|
||||
<!-- Expand/collapse handle -->
|
||||
<div
|
||||
v-if="showExpandCollapseBtn"
|
||||
bg="neutral-100 dark:[rgba(0,0,0,0.3)]"
|
||||
rounded-xl
|
||||
:class="[
|
||||
|
||||
Reference in New Issue
Block a user