diff options
Diffstat (limited to 'apps/settings/src/components')
-rw-r--r-- | apps/settings/src/components/AdminAI.vue | 22 | ||||
-rw-r--r-- | apps/settings/src/components/PersonalInfo/BlueskySection.vue | 64 |
2 files changed, 80 insertions, 6 deletions
diff --git a/apps/settings/src/components/AdminAI.vue b/apps/settings/src/components/AdminAI.vue index 044ebd9183e..0d3e9154bb9 100644 --- a/apps/settings/src/components/AdminAI.vue +++ b/apps/settings/src/components/AdminAI.vue @@ -11,16 +11,17 @@ @update:modelValue="saveChanges"> {{ t('settings', 'Allow AI usage for guest users') }} </NcCheckboxRadioSwitch> + <h3>{{ t('settings', 'Provider for Task types') }}</h3> <template v-for="type in taskProcessingTaskTypes"> - <div :key="type"> - <h3>{{ t('settings', 'Task:') }} {{ type.name }}</h3> - <p>{{ type.description }}</p> + <div :key="type" class="tasktype-item"> + <p class="tasktype-name"> + {{ type.name }} + </p> <NcCheckboxRadioSwitch v-model="settings['ai.taskprocessing_type_preferences'][type.id]" type="switch" @update:modelValue="saveChanges"> {{ t('settings', 'Enable') }} - </NcCheckboxRadioSwitch> - <NcSelect v-model="settings['ai.taskprocessing_provider_preferences'][type.id]" + </NcCheckboxRadioSwitch><NcSelect v-model="settings['ai.taskprocessing_provider_preferences'][type.id]" class="provider-select" :clearable="false" :disabled="!settings['ai.taskprocessing_type_preferences'][type.id]" @@ -33,7 +34,6 @@ {{ taskProcessingProviders.find(p => p.id === label)?.name }} </template> </NcSelect> - <p> </p> </div> </template> <template v-if="!hasTaskProcessing"> @@ -244,4 +244,14 @@ export default { .provider-select { min-width: 350px !important; } + +.tasktype-item { + display: flex; + align-items: center; + gap: 8px; + .tasktype-name { + flex: 1; + margin: 0; + } +} </style> diff --git a/apps/settings/src/components/PersonalInfo/BlueskySection.vue b/apps/settings/src/components/PersonalInfo/BlueskySection.vue new file mode 100644 index 00000000000..65223d1ab53 --- /dev/null +++ b/apps/settings/src/components/PersonalInfo/BlueskySection.vue @@ -0,0 +1,64 @@ +<!-- + - SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> + +<template> + <AccountPropertySection v-bind.sync="value" + :readable="readable" + :on-validate="onValidate" + :placeholder="t('settings', 'Bluesky handle')" /> +</template> + +<script setup lang="ts"> +import type { AccountProperties } from '../../constants/AccountPropertyConstants.js' + +import { loadState } from '@nextcloud/initial-state' +import { t } from '@nextcloud/l10n' +import { ref } from 'vue' +import { NAME_READABLE_ENUM } from '../../constants/AccountPropertyConstants.ts' +import AccountPropertySection from './shared/AccountPropertySection.vue' + +const { bluesky } = loadState<AccountProperties>('settings', 'personalInfoParameters') + +const value = ref({ ...bluesky }) +const readable = NAME_READABLE_ENUM[bluesky.name] + +/** + * Validate that the text might be a bluesky handle + * @param text The potential bluesky handle + */ +function onValidate(text: string): boolean { + if (text === '') return true + + const lowerText = text.toLowerCase() + + if (lowerText === 'bsky.social') { + // Standalone bsky.social is invalid + return false + } + + if (lowerText.endsWith('.bsky.social')) { + // Enforce format: exactly one label + '.bsky.social' + const parts = lowerText.split('.') + + // Must be in form: [username, 'bsky', 'social'] + if (parts.length !== 3 || parts[1] !== 'bsky' || parts[2] !== 'social') { + return false + } + + const username = parts[0] + const validateRegex = /^[a-z0-9][a-z0-9-]{2,17}$/ + return validateRegex.test(username) + } + + // Else, treat as a custom domain + try { + const url = new URL(`https://${text}`) + // Ensure the parsed host matches exactly (case-insensitive already) + return url.host === lowerText + } catch { + return false + } +} +</script> |