feat(stage-ui): add edit mode for airi card (#939)
This commit is contained in:
@@ -75,6 +75,7 @@ pages:
|
||||
active: Active
|
||||
active_badge: Currently Active
|
||||
cancel: Cancel action
|
||||
save: Save changes
|
||||
card_not_found: Card not found
|
||||
character: Character
|
||||
close: Close
|
||||
@@ -89,6 +90,7 @@ pages:
|
||||
description_label: Description
|
||||
drop_here: Drop to upload
|
||||
create_card: Create a new Card
|
||||
edit_card: Edit Card
|
||||
creation:
|
||||
identity: Identity
|
||||
name: Name
|
||||
|
||||
+49
-17
@@ -12,14 +12,15 @@ import {
|
||||
DialogRoot,
|
||||
DialogTitle,
|
||||
} from 'reka-ui'
|
||||
import { computed, ref, toRaw } from 'vue'
|
||||
import { computed, ref, toRaw, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
cardId?: string // If provided, edit mode; otherwise create mode
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
}>()
|
||||
@@ -29,6 +30,9 @@ const modelValue = defineModel<boolean>()
|
||||
const { t } = useI18n()
|
||||
const cardStore = useAiriCardStore()
|
||||
|
||||
// Determine if we're in edit mode
|
||||
const isEditMode = computed(() => !!props.cardId)
|
||||
|
||||
// Tab type definition
|
||||
interface Tab {
|
||||
id: string
|
||||
@@ -112,25 +116,53 @@ function saveCard(card: Card): boolean {
|
||||
}
|
||||
showError.value = false
|
||||
|
||||
cardStore.addCard(rawCard)
|
||||
if (isEditMode.value && props.cardId) {
|
||||
// Edit mode: update existing card
|
||||
cardStore.updateCard(props.cardId, rawCard)
|
||||
}
|
||||
else {
|
||||
// Create mode: add new card
|
||||
cardStore.addCard(rawCard)
|
||||
}
|
||||
|
||||
modelValue.value = false // Close this
|
||||
return true
|
||||
}
|
||||
|
||||
// Cards data holders :
|
||||
|
||||
const card = ref<Card>({
|
||||
name: t('settings.pages.card.creation.defaults.name'),
|
||||
nickname: undefined,
|
||||
version: '1.0',
|
||||
description: '',
|
||||
notes: undefined,
|
||||
personality: t('settings.pages.card.creation.defaults.personality'),
|
||||
scenario: t('settings.pages.card.creation.defaults.scenario'),
|
||||
systemPrompt: t('settings.pages.card.creation.defaults.systemprompt'),
|
||||
postHistoryInstructions: t('settings.pages.card.creation.defaults.posthistoryinstructions'),
|
||||
greetings: [],
|
||||
messageExample: [],
|
||||
// Initialize card data - load from existing card if in edit mode
|
||||
function initializeCard(): Card {
|
||||
if (isEditMode.value && props.cardId) {
|
||||
const existingCard = cardStore.getCard(props.cardId)
|
||||
if (existingCard) {
|
||||
return { ...toRaw(existingCard) }
|
||||
}
|
||||
}
|
||||
|
||||
// Default values for create mode
|
||||
return {
|
||||
name: t('settings.pages.card.creation.defaults.name'),
|
||||
nickname: undefined,
|
||||
version: '1.0',
|
||||
description: '',
|
||||
notes: undefined,
|
||||
personality: t('settings.pages.card.creation.defaults.personality'),
|
||||
scenario: t('settings.pages.card.creation.defaults.scenario'),
|
||||
systemPrompt: t('settings.pages.card.creation.defaults.systemprompt'),
|
||||
postHistoryInstructions: t('settings.pages.card.creation.defaults.posthistoryinstructions'),
|
||||
greetings: [],
|
||||
messageExample: [],
|
||||
}
|
||||
}
|
||||
|
||||
const card = ref<Card>(initializeCard())
|
||||
|
||||
// Reinitialize when cardId changes or dialog opens
|
||||
watch(() => [props.modelValue, props.cardId], () => {
|
||||
if (props.modelValue) {
|
||||
card.value = initializeCard()
|
||||
}
|
||||
})
|
||||
|
||||
function makeComputed<T extends keyof Card>(
|
||||
@@ -179,7 +211,7 @@ const cardPostHistoryInstructions = makeComputed('postHistoryInstructions')
|
||||
<DialogContent class="fixed left-1/2 top-1/2 z-100 m-0 max-h-[90vh] max-w-6xl w-[92vw] flex flex-col overflow-auto border border-neutral-200 rounded-xl bg-white p-5 shadow-xl 2xl:w-[60vw] lg:w-[80vw] md:w-[85vw] xl:w-[70vw] -translate-x-1/2 -translate-y-1/2 data-[state=closed]:animate-contentHide data-[state=open]:animate-contentShow dark:border-neutral-700 dark:bg-neutral-800 sm:p-6">
|
||||
<div class="w-full flex flex-col gap-5">
|
||||
<DialogTitle text-2xl font-normal class="from-primary-500 to-primary-400 bg-gradient-to-r bg-clip-text text-transparent">
|
||||
{{ t("settings.pages.card.create_card") }}
|
||||
{{ isEditMode ? t("settings.pages.card.edit_card") : t("settings.pages.card.create_card") }}
|
||||
</DialogTitle>
|
||||
|
||||
<!-- Dialog tabs -->
|
||||
@@ -255,7 +287,7 @@ const cardPostHistoryInstructions = makeComputed('postHistoryInstructions')
|
||||
<Button
|
||||
variant="primary"
|
||||
icon="i-solar:check-circle-bold-duotone"
|
||||
:label="t('settings.pages.card.creation.create')"
|
||||
:label="isEditMode ? t('settings.pages.card.save') : t('settings.pages.card.creation.create')"
|
||||
:disabled="false"
|
||||
@click="saveCard(card)"
|
||||
/>
|
||||
|
||||
@@ -17,6 +17,7 @@ const emit = defineEmits<{
|
||||
(e: 'select'): void
|
||||
(e: 'activate'): void
|
||||
(e: 'delete'): void
|
||||
(e: 'edit'): void
|
||||
}>()
|
||||
</script>
|
||||
|
||||
@@ -47,8 +48,17 @@ const emit = defineEmits<{
|
||||
<h3 flex-1 truncate text-lg font-normal>
|
||||
{{ name }}
|
||||
</h3>
|
||||
<div v-if="isActive" shrink-0 rounded-md p-1 bg="primary-100 dark:primary-900/40" text="primary-600 dark:primary-400">
|
||||
<div i-solar:check-circle-bold-duotone text-sm />
|
||||
<div flex shrink-0 items-center gap-2>
|
||||
<button
|
||||
rounded-lg p-1 text-neutral-500 transition-colors dark:text-neutral-400 hover="bg-neutral-200 dark:bg-neutral-700/50"
|
||||
title="Edit card"
|
||||
@click.stop="emit('edit')"
|
||||
>
|
||||
<div i-solar:pen-2-bold-duotone text-sm />
|
||||
</button>
|
||||
<div v-if="isActive" rounded-md p-1 bg="primary-100 dark:primary-900/40" text="primary-600 dark:primary-400">
|
||||
<div i-solar:check-circle-bold-duotone text-sm />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ const { cards, activeCardId } = storeToRefs(cardStore)
|
||||
|
||||
// Currently selected card ID (different from active card ID)
|
||||
const selectedCardId = ref<string>('')
|
||||
// Currently editing card ID
|
||||
const editingCardId = ref<string>('')
|
||||
// Dialog state
|
||||
const isCardDialogOpen = ref(false)
|
||||
const isCardCreationDialogOpen = ref(false)
|
||||
@@ -116,11 +118,27 @@ function confirmDelete(id: string) {
|
||||
}
|
||||
|
||||
function handleSelectCard(cardId: string) {
|
||||
// Verify card exists before opening dialog
|
||||
if (!cards.value.has(cardId)) {
|
||||
console.error(`Card with id ${cardId} not found`)
|
||||
return
|
||||
}
|
||||
selectedCardId.value = cardId
|
||||
isCardDialogOpen.value = true
|
||||
}
|
||||
|
||||
function handleEditCard(cardId: string) {
|
||||
// Verify card exists before opening edit dialog
|
||||
if (!cards.value.has(cardId)) {
|
||||
console.error(`Card with id ${cardId} not found`)
|
||||
return
|
||||
}
|
||||
editingCardId.value = cardId
|
||||
isCardCreationDialogOpen.value = true
|
||||
}
|
||||
|
||||
function handleCardCreationDialog() {
|
||||
editingCardId.value = '' // Clear editing state for new card creation
|
||||
isCardCreationDialogOpen.value = true
|
||||
}
|
||||
|
||||
@@ -129,6 +147,13 @@ function activateCard(id: string) {
|
||||
activeCardId.value = id
|
||||
}
|
||||
|
||||
// Clear editing state when creation/edit dialog closes
|
||||
watch(isCardCreationDialogOpen, (isOpen) => {
|
||||
if (!isOpen) {
|
||||
editingCardId.value = ''
|
||||
}
|
||||
})
|
||||
|
||||
// Card version number
|
||||
function getVersionNumber(id: string) {
|
||||
const card = cards.value.get(id)
|
||||
@@ -241,6 +266,7 @@ function getModuleShortName(id: string, module: 'consciousness' | 'voice') {
|
||||
@select="handleSelectCard(item.id)"
|
||||
@activate="activateCard(item.id)"
|
||||
@delete="confirmDelete(item.id)"
|
||||
@edit="handleEditCard(item.id)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -281,9 +307,10 @@ function getModuleShortName(id: string, module: 'consciousness' | 'voice') {
|
||||
:card-id="selectedCardId"
|
||||
/>
|
||||
|
||||
<!-- Card detail dialog -->
|
||||
<!-- Card creation/edit dialog -->
|
||||
<CardCreationDialog
|
||||
v-model="isCardCreationDialogOpen"
|
||||
:card-id="editingCardId"
|
||||
/>
|
||||
|
||||
<!-- Background decoration -->
|
||||
|
||||
@@ -84,6 +84,20 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
cards.value.delete(id)
|
||||
}
|
||||
|
||||
const updateCard = (id: string, updates: AiriCard | Card | ccv3.CharacterCardV3) => {
|
||||
const existingCard = cards.value.get(id)
|
||||
if (!existingCard)
|
||||
return false
|
||||
|
||||
const updatedCard = {
|
||||
...existingCard,
|
||||
...updates,
|
||||
}
|
||||
|
||||
cards.value.set(id, newAiriCard(updatedCard))
|
||||
return true
|
||||
}
|
||||
|
||||
const getCard = (id: string) => {
|
||||
return cards.value.get(id)
|
||||
}
|
||||
@@ -223,6 +237,7 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
activeCardId,
|
||||
addCard,
|
||||
removeCard,
|
||||
updateCard,
|
||||
getCard,
|
||||
resetState,
|
||||
initialize,
|
||||
|
||||
Reference in New Issue
Block a user