feat: airi card (#98)
This commit is contained in:
@@ -70,6 +70,27 @@ settings:
|
||||
microphone: Microphone
|
||||
models: Model
|
||||
pages:
|
||||
card:
|
||||
title: Airi Card
|
||||
description: Use Airi character card presets
|
||||
upload: Upload
|
||||
delete: Delete
|
||||
active: Active
|
||||
activate: Activate
|
||||
delete_card: Delete Card
|
||||
delete_confirmation: Are you sure you want to delete this card?
|
||||
card_not_found: Card not found
|
||||
created_by: created by
|
||||
creator_notes: Creator Notes
|
||||
description_label: Description
|
||||
character: Character
|
||||
personality: Personality
|
||||
scenario: Scenario
|
||||
systemprompt: System Prompt
|
||||
posthistoryinstructions: Post-History Instructions
|
||||
modules: Modules
|
||||
voice_id: Voice ID
|
||||
cancel: Cancel
|
||||
memory:
|
||||
description: Where memories got stored, and organized
|
||||
title: Memory
|
||||
|
||||
@@ -56,6 +56,27 @@ settings:
|
||||
microphone: 麦克风
|
||||
models: 模型
|
||||
pages:
|
||||
card:
|
||||
title: Airi 角色卡
|
||||
description: 使用 Airi 角色卡预设
|
||||
voice_id: 声音 ID
|
||||
upload: 上传
|
||||
delete: 删除
|
||||
active: 已激活
|
||||
activate: 激活
|
||||
delete_card: 删除角色卡
|
||||
delete_confirmation: 确定要删除这张角色卡吗?
|
||||
card_not_found: 未找到角色卡
|
||||
created_by: 创建者
|
||||
creator_notes: 创建者笔记
|
||||
description_label: 描述
|
||||
character: 角色设定
|
||||
personality: 性格
|
||||
scenario: 场景
|
||||
systemprompt: 系统提示词
|
||||
posthistoryinstructions: 历史提示指令
|
||||
modules: 模块
|
||||
cancel: 取消
|
||||
memory:
|
||||
description: 存放记忆的地方,以及策略
|
||||
title: 记忆体
|
||||
@@ -294,51 +315,51 @@ settings:
|
||||
presets:
|
||||
- colors:
|
||||
- Airi 绿
|
||||
description: The default greenish theme color, brought by Airi to you!
|
||||
description: 默认的绿色主题,由 Airi 为您带来!
|
||||
title: 默认颜色
|
||||
- colors:
|
||||
- Taupe
|
||||
- Beige
|
||||
- Ash Grey
|
||||
- Light Taupe
|
||||
- Ivory
|
||||
- Olive Grey
|
||||
- Sand
|
||||
- Warm Grey
|
||||
description: Soft, muted tones inspired by Giorgio Morandi's paintings
|
||||
- 灰褐色
|
||||
- 米色
|
||||
- 灰白色
|
||||
- 浅灰褐色
|
||||
- 象牙色
|
||||
- 橄榄灰
|
||||
- 沙色
|
||||
- 暖灰色
|
||||
description: 受乔治·莫兰迪绘画启发的柔和、低调的色调
|
||||
title: Morandi 颜色
|
||||
- colors:
|
||||
- Sky Blue
|
||||
- Mist
|
||||
- Sand
|
||||
- Moss Green
|
||||
- Water Lily
|
||||
- Wheat
|
||||
- Slate Blue
|
||||
- Sage
|
||||
description: Impressionist palette inspired by Claude Monet's works
|
||||
- 天蓝色
|
||||
- 薄雾色
|
||||
- 沙色
|
||||
- 苔藓绿
|
||||
- 睡莲色
|
||||
- 小麦色
|
||||
- 板岩蓝
|
||||
- 鼠尾草色
|
||||
description: 受克劳德·莫奈作品启发的印象派调色板
|
||||
title: 莫奈颜色
|
||||
- colors:
|
||||
- Tan
|
||||
- Warm Taupe
|
||||
- Umber
|
||||
- Coffee
|
||||
- Bronze
|
||||
- Gold
|
||||
- Mustard
|
||||
- Amber
|
||||
description: Traditional Japanese color palette
|
||||
- 棕褐色
|
||||
- 暖灰褐色
|
||||
- 赭色
|
||||
- 咖啡色
|
||||
- 青铜色
|
||||
- 金色
|
||||
- 芥末色
|
||||
- 琥珀色
|
||||
description: 传统日本色彩调色板
|
||||
title: 日本颜色
|
||||
- colors:
|
||||
- Nordic Blue
|
||||
- Ice
|
||||
- Fjord
|
||||
- Steel
|
||||
- Glacier
|
||||
- Slate
|
||||
- Cloud
|
||||
- Stone
|
||||
description: Scandinavian minimalist color scheme
|
||||
- 北欧蓝
|
||||
- 冰色
|
||||
- 峡湾色
|
||||
- 钢铁色
|
||||
- 冰川色
|
||||
- 板岩色
|
||||
- 云色
|
||||
- 石头色
|
||||
description: 北欧极简主义配色方案
|
||||
title: 北欧颜色
|
||||
- colors:
|
||||
- 霞光红
|
||||
@@ -349,7 +370,8 @@ settings:
|
||||
- 缃色
|
||||
- 青冥
|
||||
- 赭石
|
||||
description: Traditional Chinese colors, derived from ancient textiles, porcelain and paintings
|
||||
description: >-
|
||||
中国传统色彩,源自古代纺织品、瓷器和绘画
|
||||
title: 中国传统颜色
|
||||
title: 预设
|
||||
title: 外观
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"@pixiv/three-vrm": "^3.3.6",
|
||||
"@pixiv/three-vrm-animation": "^3.3.6",
|
||||
"@pixiv/three-vrm-core": "^3.3.6",
|
||||
"@proj-airi/ccc": "workspace:^",
|
||||
"@proj-airi/drizzle-duckdb-wasm": "workspace:^",
|
||||
"@proj-airi/elevenlabs": "workspace:^",
|
||||
"@proj-airi/provider-transformers": "workspace:^",
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { Collapsable } from '@proj-airi/stage-ui/components'
|
||||
|
||||
defineProps<{
|
||||
withDefaults(defineProps<{
|
||||
title: string
|
||||
icon: string
|
||||
innerClass?: string
|
||||
}>()
|
||||
expand?: boolean
|
||||
}>(), {
|
||||
expand: true,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Collapsable default>
|
||||
<Collapsable :default="expand">
|
||||
<template #trigger="slotProps">
|
||||
<button
|
||||
class="setting-bar"
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
<script setup lang="ts">
|
||||
import type { AiriCard } from '@proj-airi/stage-ui/stores'
|
||||
|
||||
import { Button, Section } from '@proj-airi/stage-ui/components'
|
||||
import { useAiriCardStore } from '@proj-airi/stage-ui/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import {
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogOverlay,
|
||||
AlertDialogPortal,
|
||||
AlertDialogRoot,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from 'radix-vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
interface Props {
|
||||
cardId: string
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'activate'): void
|
||||
(e: 'delete'): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const cardStore = useAiriCardStore()
|
||||
const { getCard, removeCard } = cardStore
|
||||
const { activeCardId } = storeToRefs(cardStore)
|
||||
|
||||
// Get current card
|
||||
const card = computed<AiriCard | undefined>(() => getCard(props.cardId))
|
||||
|
||||
// Get module settings
|
||||
const moduleSettings = computed(() => {
|
||||
const airiExt = card.value?.extensions?.airi?.modules
|
||||
return {
|
||||
consciousness: airiExt?.consciousness?.model || '',
|
||||
speech: airiExt?.speech?.model || '',
|
||||
voice: airiExt?.speech?.voice_id || '',
|
||||
}
|
||||
})
|
||||
|
||||
// Get character settings
|
||||
const characterSettings = computed(() => {
|
||||
if (!card.value)
|
||||
return {}
|
||||
|
||||
return {
|
||||
personality: card.value.personality,
|
||||
scenario: card.value.scenario,
|
||||
systemPrompt: card.value.systemPrompt,
|
||||
postHistoryInstructions: card.value.postHistoryInstructions,
|
||||
}
|
||||
})
|
||||
|
||||
// Check if card is active
|
||||
const isActive = computed(() => props.cardId === activeCardId.value)
|
||||
|
||||
// Activate card
|
||||
function activateCard() {
|
||||
activeCardId.value = props.cardId
|
||||
emit('activate')
|
||||
}
|
||||
|
||||
// Animation control for card activation
|
||||
const isActivating = ref(false)
|
||||
|
||||
function handleActivate() {
|
||||
isActivating.value = true
|
||||
setTimeout(() => {
|
||||
activateCard()
|
||||
isActivating.value = false
|
||||
activeCardId.value = 'default'
|
||||
}, 300)
|
||||
}
|
||||
|
||||
// Delete card confirmation
|
||||
const showDeleteConfirm = ref(false)
|
||||
|
||||
function handleDeleteConfirm() {
|
||||
if (card.value) {
|
||||
removeCard(props.cardId)
|
||||
emit('delete')
|
||||
activateCard()
|
||||
}
|
||||
showDeleteConfirm.value = false
|
||||
}
|
||||
|
||||
function hightlightTagToHtml(text: string) {
|
||||
return text?.replace(/\{\{(.*?)\}\}/g, '<span class="bg-primary-500/20 inline-block">{{ $1 }}</span>').trim()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="card"
|
||||
bg="neutral-50 dark:[rgba(0,0,0,0.3)]"
|
||||
rounded-xl p-5 flex="~ col gap-5"
|
||||
border="~ neutral-200/50 dark:neutral-700/30"
|
||||
shadow="sm dark:md"
|
||||
transition="all duration-300"
|
||||
class="backdrop-blur-sm"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div flex="~ col" gap-3>
|
||||
<div flex="~ row" items-center justify-between>
|
||||
<div>
|
||||
<h1 text-2xl font-bold class="from-primary-500 to-primary-400 bg-gradient-to-r bg-clip-text text-transparent">
|
||||
{{ card.name }}
|
||||
</h1>
|
||||
<div mt-1 text-sm text-neutral-500 dark:text-neutral-400>
|
||||
v{{ card.version }}
|
||||
<template v-if="card.creator">
|
||||
· {{ t('settings.pages.card.created_by') }} <span font-medium>{{ card.creator }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action buttons -->
|
||||
<div flex="~ row" gap-2>
|
||||
<!-- Delete button -->
|
||||
<AlertDialogRoot v-if="props.cardId !== 'default'" v-model:open="showDeleteConfirm">
|
||||
<AlertDialogTrigger as-child>
|
||||
<Button
|
||||
variant="danger"
|
||||
:label="t('settings.pages.card.delete')"
|
||||
/>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay class="fixed inset-0 z-50 bg-black/50" />
|
||||
<AlertDialogContent
|
||||
class="fixed left-1/2 top-1/2 z-50 max-w-md w-full border border-neutral-200 rounded-xl bg-white p-6 shadow-xl -translate-x-1/2 -translate-y-1/2 dark:border-neutral-700 dark:bg-neutral-800"
|
||||
>
|
||||
<AlertDialogTitle class="mb-4 text-xl font-bold">
|
||||
{{ t('settings.pages.card.delete_card') }}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription class="mb-6">
|
||||
{{ t('settings.pages.card.delete_confirmation') }} <b>"{{ card.name }}"</b>
|
||||
</AlertDialogDescription>
|
||||
|
||||
<div class="flex flex-row justify-end gap-3">
|
||||
<AlertDialogCancel as-child>
|
||||
<Button
|
||||
variant="secondary"
|
||||
:label="t('settings.pages.card.cancel')"
|
||||
@click="() => showDeleteConfirm = false"
|
||||
/>
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction as-child>
|
||||
<Button
|
||||
variant="danger"
|
||||
:label="t('settings.pages.card.delete')"
|
||||
@click="handleDeleteConfirm"
|
||||
/>
|
||||
</AlertDialogAction>
|
||||
</div>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogPortal>
|
||||
</AlertDialogRoot>
|
||||
|
||||
<!-- Activation button -->
|
||||
<Button
|
||||
variant="primary"
|
||||
:label="isActive ? t('settings.pages.card.active') : t('settings.pages.card.activate')"
|
||||
:disabled="isActive"
|
||||
:class="{ 'animate-pulse': isActivating }"
|
||||
@click="handleActivate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Creator notes -->
|
||||
<Section v-if="card.notes" :title="t('settings.pages.card.creator_notes')" icon="i-solar:notes-bold-duotone">
|
||||
<div
|
||||
bg="white/60 dark:black/30"
|
||||
whitespace-pre-line rounded-lg p-4
|
||||
text-neutral-700 dark:text-neutral-300
|
||||
border="~ neutral-200/50 dark:neutral-700/30"
|
||||
transition="all duration-200"
|
||||
hover="bg-white/80 dark:bg-black/40"
|
||||
v-html="hightlightTagToHtml(card.notes)"
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<!-- Description section -->
|
||||
<Section v-if="card.description" :title="t('settings.pages.card.description_label')" icon="i-solar:document-text-bold-duotone">
|
||||
<div
|
||||
bg="white/60 dark:black/30"
|
||||
whitespace-pre-line
|
||||
rounded-lg
|
||||
p-4
|
||||
text="neutral-600 dark:neutral-300"
|
||||
border="~ neutral-200/50 dark:neutral-700/30"
|
||||
v-html="hightlightTagToHtml(card.description)"
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<!-- Character -->
|
||||
<template v-if="Object.values(characterSettings).some(value => !!value)">
|
||||
<Section :title="t('settings.pages.card.character')" icon="i-solar:user-rounded-bold-duotone">
|
||||
<div flex="~ col" gap-4>
|
||||
<template v-for="(value, key) in characterSettings" :key="key">
|
||||
<div v-if="value" flex="~ col" gap-2>
|
||||
<h2 text-lg text-neutral-500 font-medium dark:text-neutral-400>
|
||||
{{ t(`settings.pages.card.${key.toLowerCase()}`) }}
|
||||
</h2>
|
||||
<div
|
||||
bg="white/60 dark:black/30"
|
||||
border="~ neutral-200/50 dark:neutral-700/30"
|
||||
transition="all duration-200"
|
||||
hover="bg-white/80 dark:bg-black/40"
|
||||
max-h-60 overflow-auto whitespace-pre-line rounded-lg p-3 text-neutral-700 dark:text-neutral-300
|
||||
v-html="hightlightTagToHtml(value)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</Section>
|
||||
</template>
|
||||
|
||||
<!-- Modules -->
|
||||
<Section :title="t('settings.pages.card.modules')" icon="i-solar:tuning-square-bold-duotone">
|
||||
<div grid="~ cols-1 sm:cols-3" gap-4>
|
||||
<div
|
||||
flex="~ col"
|
||||
bg="white/60 dark:black/30"
|
||||
gap-1 rounded-lg p-3
|
||||
border="~ neutral-200/50 dark:neutral-700/30"
|
||||
transition="all duration-200"
|
||||
hover="bg-white/80 dark:bg-black/40"
|
||||
>
|
||||
<span flex="~ row" items-center gap-2 text-sm text-neutral-500 dark:text-neutral-400>
|
||||
<div i-lucide:ghost />
|
||||
{{ t('settings.pages.modules.consciousness.title') }}
|
||||
</span>
|
||||
<div truncate font-medium>
|
||||
{{ moduleSettings.consciousness }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
flex="~ col"
|
||||
bg="white/60 dark:black/30"
|
||||
gap-2 rounded-lg p-3
|
||||
border="~ neutral-200/50 dark:neutral-700/30"
|
||||
transition="all duration-200"
|
||||
hover="bg-white/80 dark:bg-black/40"
|
||||
>
|
||||
<span flex="~ row" items-center gap-2 text-sm text-neutral-500 dark:text-neutral-400>
|
||||
<div i-lucide:mic />
|
||||
{{ t('settings.pages.modules.speech.title') }}
|
||||
</span>
|
||||
<div truncate font-medium>
|
||||
{{ moduleSettings.speech }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
flex="~ col"
|
||||
bg="white/60 dark:black/30"
|
||||
gap-2 rounded-lg p-3
|
||||
border="~ neutral-200/50 dark:neutral-700/30"
|
||||
transition="all duration-200"
|
||||
hover="bg-white/80 dark:bg-black/40"
|
||||
>
|
||||
<span flex="~ row" items-center gap-2 text-sm text-neutral-500 dark:text-neutral-400>
|
||||
<div i-solar:music-notes-bold-duotone />
|
||||
{{ t('settings.pages.card.voice_id') }}
|
||||
</span>
|
||||
<div truncate font-medium>
|
||||
{{ moduleSettings.voice }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
bg="neutral-50/50 dark:neutral-900/50"
|
||||
rounded-xl p-8 text-center
|
||||
border="~ neutral-200/50 dark:neutral-700/30"
|
||||
shadow="sm"
|
||||
>
|
||||
<div i-solar:card-search-broken mx-auto mb-3 text-6xl text-neutral-400 />
|
||||
{{ t('settings.pages.card.card_not_found') }}
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,153 @@
|
||||
<script setup lang="ts">
|
||||
import type { ccv3 } from '@proj-airi/ccc'
|
||||
|
||||
import { Button } from '@proj-airi/stage-ui/components/Button'
|
||||
import { RadioCardDetailManySelect } from '@proj-airi/stage-ui/components/Form'
|
||||
import { useAiriCardStore } from '@proj-airi/stage-ui/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import AiriCardView from './components/AiriCardView.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
const cardStore = useAiriCardStore()
|
||||
const { addCard } = cardStore
|
||||
const { cards, activeCardId } = storeToRefs(cardStore)
|
||||
|
||||
// Currently selected card ID (different from active card ID)
|
||||
const selectedCardId = ref<string>(activeCardId.value || '')
|
||||
|
||||
// Card list data structure
|
||||
interface CardListItem {
|
||||
id: string
|
||||
name: string
|
||||
description?: string
|
||||
deprecated?: boolean
|
||||
customizable?: boolean
|
||||
}
|
||||
|
||||
// Transform cards Map to array for display
|
||||
const cardsArray = computed<CardListItem[]>(() =>
|
||||
Array.from(cards.value.entries()).map(([id, card]) => ({
|
||||
id,
|
||||
name: card.name,
|
||||
description: card.description,
|
||||
})),
|
||||
)
|
||||
|
||||
/**
|
||||
* Handles card file upload and processing
|
||||
*/
|
||||
async function handleUpload() {
|
||||
const fileInput = document.createElement('input')
|
||||
fileInput.type = 'file'
|
||||
fileInput.accept = '.json'
|
||||
|
||||
fileInput.onchange = async (event: Event) => {
|
||||
const file = (event.target as HTMLInputElement).files?.[0]
|
||||
if (!file)
|
||||
return
|
||||
|
||||
try {
|
||||
const content = await file.text()
|
||||
const cardJSON = JSON.parse(content) as ccv3.CharacterCardV3
|
||||
|
||||
// Add card and select it
|
||||
selectedCardId.value = addCard(cardJSON)
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error processing card file:', error)
|
||||
}
|
||||
}
|
||||
|
||||
fileInput.click()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected card as active
|
||||
*/
|
||||
function activateSelectedCard() {
|
||||
if (selectedCardId.value)
|
||||
activeCardId.value = selectedCardId.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles card deletion
|
||||
*/
|
||||
function handleCardDelete() {
|
||||
// Reset selected card ID if it's the one being deleted
|
||||
selectedCardId.value = cardsArray.value.length > 0 ? cardsArray.value[0].id : ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-motion
|
||||
flex="~ row" items-center gap-2
|
||||
:initial="{ opacity: 0, x: 10 }"
|
||||
:enter="{ opacity: 1, x: 0 }"
|
||||
:leave="{ opacity: 0, x: -10 }"
|
||||
:duration="250"
|
||||
>
|
||||
<button @click="router.back()">
|
||||
<div i-solar:alt-arrow-left-line-duotone text-2xl />
|
||||
</button>
|
||||
<h1 relative>
|
||||
<div absolute left-0 top-0 translate-y="[-80%]">
|
||||
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.title') }}</span>
|
||||
</div>
|
||||
<div text-nowrap text-3xl font-semibold>
|
||||
{{ t('settings.pages.card.title') }}
|
||||
</div>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div bg="neutral-50 dark:[rgba(0,0,0,0.3)]" rounded-xl p-4 flex="~ col gap-4">
|
||||
<!-- Toolbar -->
|
||||
<div flex="~ col" gap-6>
|
||||
<div flex="~ row gap-2 flex-wrap">
|
||||
<!-- Upload button -->
|
||||
<Button
|
||||
variant="primary"
|
||||
icon="i-solar:upload-line-duotone"
|
||||
:label="t('settings.pages.card.upload')"
|
||||
@click="handleUpload"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Card selection -->
|
||||
<template v-if="cards.size > 0">
|
||||
<RadioCardDetailManySelect
|
||||
v-model="selectedCardId"
|
||||
:items="cardsArray"
|
||||
:expand-button-text="t('settings.pages.modules.consciousness.sections.section.provider-model-selection.expand')"
|
||||
:collapse-button-text="t('settings.pages.modules.consciousness.sections.section.provider-model-selection.collapse')"
|
||||
:show-more="false"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Card content area -->
|
||||
<div v-if="selectedCardId && cards.size > 0" mt-6>
|
||||
<AiriCardView
|
||||
:card-id="selectedCardId"
|
||||
@activate="activateSelectedCard"
|
||||
@delete="handleCardDelete"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Background decoration -->
|
||||
<div text="neutral-200/50 dark:neutral-600/20" pointer-events-none fixed bottom-0 right-0 z--1 translate-x-10 translate-y-10>
|
||||
<div text="40" i-lucide:id-card />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
stageTransition:
|
||||
name: slide
|
||||
</route>
|
||||
@@ -60,6 +60,12 @@ const removeBeforeEach = router.beforeEach(async (_, __, next) => {
|
||||
})
|
||||
|
||||
const settings = computed(() => [
|
||||
{
|
||||
title: t('settings.pages.card.title'),
|
||||
description: t('settings.pages.card.description'),
|
||||
icon: 'i-lucide:card',
|
||||
to: '/settings/airi-card',
|
||||
},
|
||||
{
|
||||
title: t('settings.pages.modules.title'),
|
||||
description: t('settings.pages.modules.description'),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# @moeru-ai/ccc
|
||||
# @proj-airi/ccc
|
||||
|
||||
Create Character Card in a modular way.
|
||||
|
||||
|
||||
@@ -74,10 +74,48 @@ interface CardDescription {
|
||||
description?: string
|
||||
}
|
||||
|
||||
interface CardExtra {
|
||||
/**
|
||||
* Character's personality traits and behavioral patterns
|
||||
* Used to define how the character should act and respond
|
||||
*/
|
||||
personality?: string
|
||||
|
||||
/**
|
||||
* Background context and setting for the character
|
||||
* Provides the environment and situation the character exists in
|
||||
*/
|
||||
scenario?: string
|
||||
|
||||
/**
|
||||
* Core system instructions for the character's behavior
|
||||
* Defines fundamental rules and context for the AI model
|
||||
*/
|
||||
systemPrompt?: string
|
||||
|
||||
/**
|
||||
* Instructions to process after chat history
|
||||
* Helps maintain character consistency across conversations
|
||||
*/
|
||||
postHistoryInstructions?: string
|
||||
|
||||
/**
|
||||
* Categorization labels for the character
|
||||
* Used for filtering and organization
|
||||
*/
|
||||
tags?: string[]
|
||||
|
||||
/**
|
||||
* Sample conversation snippets showing character interactions
|
||||
* Demonstrates expected conversation patterns
|
||||
*/
|
||||
messageExample?: Message[][]
|
||||
}
|
||||
|
||||
/**
|
||||
* Moeru-AI Character Card
|
||||
*/
|
||||
export type Card = CardAdditional & CardCore & CardDescription & CardMeta
|
||||
export type Card = CardAdditional & CardCore & CardDescription & CardMeta & CardExtra
|
||||
|
||||
export type CardFn<T extends Record<string, unknown> = Record<string, unknown>> = (data: T) => Card
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ function createCardData(data: Card): CharacterCardV3['data'] {
|
||||
return {
|
||||
name: data.name,
|
||||
nickname: data.nickname,
|
||||
description: data.description ?? '', // TODO: improve description // FIXME: important
|
||||
personality: '', // TODO: add personality
|
||||
scenario: '', // TODO: add scenario
|
||||
description: data.description ?? '',
|
||||
personality: data.personality ?? '',
|
||||
scenario: data.scenario ?? '',
|
||||
first_mes: data.greetings?.[0] ?? '',
|
||||
alternate_greetings: data.greetings?.slice(1) ?? [],
|
||||
group_only_greetings: data.greetingsGroupOnly ?? [],
|
||||
@@ -33,10 +33,10 @@ function createCardData(data: Card): CharacterCardV3['data'] {
|
||||
creator: data.creator ?? '',
|
||||
creator_notes: data.notes ?? '',
|
||||
creator_notes_multilingual: data.notesMultilingual,
|
||||
system_prompt: '', // TODO: add system_prompt
|
||||
post_history_instructions: '', // TODO: add post_history_instructions
|
||||
system_prompt: data.systemPrompt ?? '',
|
||||
post_history_instructions: data.postHistoryInstructions ?? '',
|
||||
mes_example: formatMessageExample(data.messageExample),
|
||||
tags: [], // TODO: add tags
|
||||
tags: data.tags ?? [],
|
||||
extensions: createExtensions(data),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@proj-airi/ccc": "workspace:^",
|
||||
"@proj-airi/server-sdk": "workspace:^",
|
||||
"@vueuse/motion": "^3.0.3",
|
||||
"radix-vue": "^1.9.17",
|
||||
"reka-ui": "^2.1.1",
|
||||
"unist-builder": "^4.0.0",
|
||||
"xast-util-to-xml": "^4.0.0",
|
||||
|
||||
@@ -12,7 +12,6 @@ interface ButtonProps {
|
||||
label?: string // Button text label
|
||||
disabled?: boolean // Disabled state
|
||||
loading?: boolean // Loading state
|
||||
onClick?: () => void // Click handler
|
||||
variant?: ButtonVariant // Button style variant
|
||||
size?: ButtonSize // Button size variant
|
||||
block?: boolean // Full width button
|
||||
@@ -59,7 +58,6 @@ const baseClasses = computed(() => [
|
||||
<button
|
||||
:disabled="isDisabled"
|
||||
:class="baseClasses"
|
||||
@click="onClick"
|
||||
>
|
||||
<div class="flex flex-row items-center justify-center gap-2">
|
||||
<div v-if="loading" class="i-lucide:loader-circle animate-spin" />
|
||||
|
||||
@@ -98,7 +98,7 @@ function toggleExpansion() {
|
||||
<!-- Description with ellipsis (limited to 2 lines) -->
|
||||
<TransitionVertical>
|
||||
<div
|
||||
v-if="!isExpanded"
|
||||
v-if="!isExpanded || !showExpandCollapse"
|
||||
class="line-clamp-2 cursor-pointer text-xs"
|
||||
:class="[
|
||||
modelValue === value
|
||||
|
||||
@@ -21,6 +21,7 @@ interface Props {
|
||||
customInputPlaceholder?: string
|
||||
expandButtonText?: string
|
||||
collapseButtonText?: string
|
||||
showMore?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -32,6 +33,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
customInputPlaceholder: 'Enter custom value',
|
||||
expandButtonText: 'Show more',
|
||||
collapseButtonText: 'Show less',
|
||||
showMore: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -120,7 +122,7 @@ function updateCustomValue(value: string) {
|
||||
:title="item.name"
|
||||
:description="item.description"
|
||||
:deprecated="item.deprecated"
|
||||
:show-expand-collapse="true"
|
||||
:show-expand-collapse="showMore"
|
||||
:expand-collapse-threshold="100"
|
||||
:show-custom-input="item.customizable"
|
||||
:custom-input-value="customValue"
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import type { ChatProvider } from '@xsai-ext/shared-providers'
|
||||
import type { AssistantMessage, Message } from '@xsai/shared-chat'
|
||||
import type { AssistantMessage, Message, SystemMessage } from '@xsai/shared-chat'
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { defineStore, storeToRefs } from 'pinia'
|
||||
import { ref, toRaw } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useLlmmarkerParser } from '../composables/llmmarkerParser'
|
||||
import SystemPromptV2 from '../constants/prompts/system-v2'
|
||||
import { useLLM } from '../stores/llm'
|
||||
import { asyncIteratorFromReadableStream } from '../utils/iterator'
|
||||
import { useAiriCardStore } from './modules'
|
||||
|
||||
export interface ErrorMessage {
|
||||
role: 'error'
|
||||
@@ -17,7 +16,7 @@ export interface ErrorMessage {
|
||||
|
||||
export const useChatStore = defineStore('chat', () => {
|
||||
const { stream } = useLLM()
|
||||
const { t } = useI18n()
|
||||
const { systemPrompt } = storeToRefs(useAiriCardStore())
|
||||
|
||||
const sending = ref(false)
|
||||
|
||||
@@ -63,10 +62,10 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
|
||||
const messages = ref<Array<Message | ErrorMessage>>([
|
||||
SystemPromptV2(
|
||||
t('prompt.prefix'),
|
||||
t('prompt.suffix'),
|
||||
),
|
||||
{
|
||||
role: 'system',
|
||||
content: systemPrompt.value.replace(/\{\{user\}\}/g, 'user'),
|
||||
} satisfies SystemMessage,
|
||||
])
|
||||
|
||||
const streamingMessage = ref<AssistantMessage>({ role: 'assistant', content: '' })
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
import type { Card, ccv3 } from '@proj-airi/ccc'
|
||||
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import { defineStore, storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import SystemPromptV2 from '../../constants/prompts/system-v2'
|
||||
import { useConsciousnessStore } from './consciousness'
|
||||
import { useSpeechStore } from './speech'
|
||||
|
||||
export interface AiriExtension {
|
||||
modules: {
|
||||
consciousness: {
|
||||
model: string // Example: "gpt-4o"
|
||||
}
|
||||
|
||||
speech: {
|
||||
model: string // Example: "eleven_multilingual_v2"
|
||||
voice_id: string // Example: "alloy"
|
||||
|
||||
pitch?: number
|
||||
rate?: number
|
||||
ssml?: boolean
|
||||
language?: string
|
||||
}
|
||||
|
||||
vrm?: {
|
||||
source?: 'file' | 'url'
|
||||
file?: string // Example: "vrm/model.vrm"
|
||||
url?: string // Example: "https://example.com/vrm/model.vrm"
|
||||
}
|
||||
|
||||
live2d?: {
|
||||
source?: 'file' | 'url'
|
||||
file?: string // Example: "live2d/model.json"
|
||||
url?: string // Example: "https://example.com/live2d/model.json"
|
||||
}
|
||||
}
|
||||
|
||||
agents: {
|
||||
[key: string]: { // example: minecraft
|
||||
prompt: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface AiriCard extends Card {
|
||||
extensions: {
|
||||
airi: AiriExtension
|
||||
} & Card['extensions']
|
||||
}
|
||||
|
||||
export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
const cards = useLocalStorage<Map<string, AiriCard>>('airi-cards', new Map())
|
||||
const activeCardId = useLocalStorage('airi-card-active-id', 'default')
|
||||
|
||||
const activeCard = computed(() => cards.value.get(activeCardId.value))
|
||||
|
||||
const consciousnessStore = useConsciousnessStore()
|
||||
const speechStore = useSpeechStore()
|
||||
|
||||
const {
|
||||
activeModel: activeConsciousnessModel,
|
||||
} = storeToRefs(consciousnessStore)
|
||||
|
||||
const {
|
||||
activeSpeechVoiceId,
|
||||
activeSpeechModel,
|
||||
} = storeToRefs(speechStore)
|
||||
|
||||
const addCard = (card: AiriCard | Card | ccv3.CharacterCardV3) => {
|
||||
const newCardId = crypto.randomUUID()
|
||||
cards.value.set(newCardId, newAiriCard(card))
|
||||
return newCardId
|
||||
}
|
||||
|
||||
const removeCard = (id: string) => {
|
||||
cards.value.delete(id)
|
||||
}
|
||||
|
||||
const getCard = (id: string) => {
|
||||
return cards.value.get(id)
|
||||
}
|
||||
|
||||
function resolveAiriExtension(card: Card | ccv3.CharacterCardV3): AiriExtension {
|
||||
// Get existing extension if available
|
||||
const existingExtension = ('data' in card
|
||||
? card.data?.extensions?.airi
|
||||
: card.extensions?.airi) as AiriExtension
|
||||
|
||||
// Create default modules config
|
||||
const defaultModules = {
|
||||
consciousness: {
|
||||
model: activeConsciousnessModel.value,
|
||||
},
|
||||
speech: {
|
||||
model: activeSpeechModel.value,
|
||||
voice_id: activeSpeechVoiceId.value,
|
||||
},
|
||||
}
|
||||
|
||||
// Return default if no extension exists
|
||||
if (!existingExtension) {
|
||||
return {
|
||||
modules: defaultModules,
|
||||
agents: {},
|
||||
}
|
||||
}
|
||||
|
||||
// Merge existing extension with defaults
|
||||
return {
|
||||
modules: {
|
||||
consciousness: {
|
||||
model: existingExtension.modules?.consciousness?.model ?? defaultModules.consciousness.model,
|
||||
},
|
||||
speech: {
|
||||
model: existingExtension.modules?.speech?.model ?? defaultModules.speech.model,
|
||||
voice_id: existingExtension.modules?.speech?.voice_id ?? defaultModules.speech.voice_id,
|
||||
pitch: existingExtension.modules?.speech?.pitch,
|
||||
rate: existingExtension.modules?.speech?.rate,
|
||||
ssml: existingExtension.modules?.speech?.ssml,
|
||||
language: existingExtension.modules?.speech?.language,
|
||||
},
|
||||
vrm: existingExtension.modules?.vrm,
|
||||
live2d: existingExtension.modules?.live2d,
|
||||
},
|
||||
agents: existingExtension.agents ?? {},
|
||||
}
|
||||
}
|
||||
|
||||
function newAiriCard(card: Card | ccv3.CharacterCardV3): AiriCard {
|
||||
// Handle ccv3 format if needed
|
||||
if ('data' in card) {
|
||||
const ccv3Card = card as ccv3.CharacterCardV3
|
||||
return {
|
||||
name: ccv3Card.data.name,
|
||||
version: ccv3Card.data.character_version ?? '1.0.0',
|
||||
description: ccv3Card.data.description ?? '',
|
||||
creator: ccv3Card.data.creator ?? '',
|
||||
notes: ccv3Card.data.creator_notes ?? '',
|
||||
notesMultilingual: ccv3Card.data.creator_notes_multilingual,
|
||||
personality: ccv3Card.data.personality ?? '',
|
||||
scenario: ccv3Card.data.scenario ?? '',
|
||||
greetings: [
|
||||
ccv3Card.data.first_mes,
|
||||
...(ccv3Card.data.alternate_greetings ?? []),
|
||||
],
|
||||
greetingsGroupOnly: ccv3Card.data.group_only_greetings ?? [],
|
||||
systemPrompt: ccv3Card.data.system_prompt ?? '',
|
||||
postHistoryInstructions: ccv3Card.data.post_history_instructions ?? '',
|
||||
messageExample: ccv3Card.data.mes_example
|
||||
? ccv3Card.data.mes_example
|
||||
.split('<START>\n')
|
||||
.filter(Boolean)
|
||||
.map(example => example.split('\n')
|
||||
.map((line) => {
|
||||
if (line.startsWith('{{char}}:') || line.startsWith('{{user}}:'))
|
||||
return line as `{{char}}: ${string}` | `{{user}}: ${string}`
|
||||
throw new Error(`Invalid message example format: ${line}`)
|
||||
}))
|
||||
: [],
|
||||
tags: ccv3Card.data.tags ?? [],
|
||||
extensions: {
|
||||
airi: resolveAiriExtension(ccv3Card),
|
||||
...ccv3Card.data.extensions,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...card,
|
||||
extensions: {
|
||||
airi: resolveAiriExtension(card),
|
||||
...card.extensions,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const { t } = useI18n()
|
||||
cards.value.set('default', newAiriCard({
|
||||
name: 'ReLU',
|
||||
version: '1.0.0',
|
||||
// description: 'ReLU is a simple and effective activation function that is used in many neural networks.',
|
||||
description: SystemPromptV2(
|
||||
t('prompt.prefix'),
|
||||
t('prompt.suffix'),
|
||||
).content,
|
||||
}))
|
||||
})
|
||||
|
||||
watch(activeCard, (newCard: AiriCard | undefined) => {
|
||||
if (!newCard)
|
||||
return
|
||||
|
||||
// TODO: live2d, vrm
|
||||
// TODO: Minecraft Agent, etc
|
||||
const extension = resolveAiriExtension(newCard)
|
||||
if (!extension)
|
||||
return
|
||||
|
||||
activeConsciousnessModel.value = extension?.modules?.consciousness?.model
|
||||
activeSpeechModel.value = extension?.modules?.speech?.model
|
||||
activeSpeechVoiceId.value = extension?.modules?.speech?.voice_id
|
||||
})
|
||||
|
||||
return {
|
||||
cards,
|
||||
activeCard,
|
||||
activeCardId,
|
||||
addCard,
|
||||
removeCard,
|
||||
getCard,
|
||||
|
||||
currentModels: computed(() => {
|
||||
return {
|
||||
consciousness: {
|
||||
model: activeConsciousnessModel.value,
|
||||
},
|
||||
speech: {
|
||||
model: activeSpeechModel.value,
|
||||
voice_id: activeSpeechVoiceId.value,
|
||||
},
|
||||
} satisfies AiriExtension['modules']
|
||||
}),
|
||||
|
||||
systemPrompt: computed(() => {
|
||||
const card = activeCard.value
|
||||
if (!card)
|
||||
return ''
|
||||
|
||||
const components = [
|
||||
card.systemPrompt,
|
||||
card.description,
|
||||
card.personality,
|
||||
].filter(Boolean)
|
||||
|
||||
return components.join('\n')
|
||||
}),
|
||||
}
|
||||
})
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './airi-card'
|
||||
export * from './consciousness'
|
||||
export * from './speech'
|
||||
|
||||
Generated
+24
-15
@@ -418,7 +418,7 @@ importers:
|
||||
version: 2.3.0
|
||||
'@intlify/unplugin-vue-i18n':
|
||||
specifier: ^6.0.5
|
||||
version: 6.0.5(@vue/compiler-dom@3.5.13)(eslint@9.23.0(jiti@2.4.2))(rollup@2.79.1)(typescript@5.8.2)(vue-i18n@11.1.2(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))
|
||||
version: 6.0.5(@vue/compiler-dom@3.5.13)(eslint@9.23.0(jiti@2.4.2))(rollup@4.38.0)(typescript@5.8.2)(vue-i18n@11.1.2(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))
|
||||
'@proj-airi/drizzle-duckdb-wasm':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/drizzle-duckdb-wasm
|
||||
@@ -469,7 +469,7 @@ importers:
|
||||
version: 3.0.0-beta.7(typescript@5.8.2)(vue-tsc@3.0.0-alpha.2(typescript@5.8.2))(vue@3.5.13(typescript@5.8.2))
|
||||
'@vueuse/motion':
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3(magicast@0.3.5)(rollup@2.79.1)(vue@3.5.13(typescript@5.8.2))
|
||||
version: 3.0.3(magicast@0.3.5)(rollup@4.38.0)(vue@3.5.13(typescript@5.8.2))
|
||||
electron:
|
||||
specifier: ^34.4.1
|
||||
version: 34.4.1
|
||||
@@ -490,13 +490,13 @@ importers:
|
||||
version: 3.2.0(unocss@66.1.0-beta.7(postcss@8.5.3)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.2)))
|
||||
unplugin-auto-import:
|
||||
specifier: ^19.1.2
|
||||
version: 19.1.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@2.79.1))(@vueuse/core@13.0.0(vue@3.5.13(typescript@5.8.2)))
|
||||
version: 19.1.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.38.0))(@vueuse/core@13.0.0(vue@3.5.13(typescript@5.8.2)))
|
||||
unplugin-vue-components:
|
||||
specifier: ^28.4.1
|
||||
version: 28.4.1(@babel/parser@7.26.10)(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@2.79.1))(vue@3.5.13(typescript@5.8.2))
|
||||
version: 28.4.1(@babel/parser@7.26.10)(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.38.0))(vue@3.5.13(typescript@5.8.2))
|
||||
unplugin-vue-macros:
|
||||
specifier: ^2.14.5
|
||||
version: 2.14.5(@vueuse/core@13.0.0(vue@3.5.13(typescript@5.8.2)))(esbuild@0.19.12)(rollup@2.79.1)(typescript@5.8.2)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue-tsc@3.0.0-alpha.2(typescript@5.8.2))(vue@3.5.13(typescript@5.8.2))
|
||||
version: 2.14.5(@vueuse/core@13.0.0(vue@3.5.13(typescript@5.8.2)))(esbuild@0.25.0)(rollup@4.38.0)(typescript@5.8.2)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue-tsc@3.0.0-alpha.2(typescript@5.8.2))(vue@3.5.13(typescript@5.8.2))
|
||||
unplugin-vue-markdown:
|
||||
specifier: ^28.3.1
|
||||
version: 28.3.1(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))
|
||||
@@ -505,13 +505,13 @@ importers:
|
||||
version: 0.12.0(vue-router@4.5.0(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))
|
||||
vite-bundle-visualizer:
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1(rollup@2.79.1)
|
||||
version: 1.2.1(rollup@4.38.0)
|
||||
vite-plugin-pwa:
|
||||
specifier: ^0.21.2
|
||||
version: 0.21.2(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0)
|
||||
vite-plugin-vue-devtools:
|
||||
specifier: ^7.7.2
|
||||
version: 7.7.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@2.79.1))(rollup@2.79.1)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.2))
|
||||
version: 7.7.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.38.0))(rollup@4.38.0)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.2))
|
||||
vite-plugin-vue-layouts:
|
||||
specifier: ^0.11.0
|
||||
version: 0.11.0(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue-router@4.5.0(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))
|
||||
@@ -906,6 +906,9 @@ importers:
|
||||
'@pixiv/three-vrm-core':
|
||||
specifier: ^3.3.6
|
||||
version: 3.3.6(three@0.175.0)
|
||||
'@proj-airi/ccc':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/ccc
|
||||
'@proj-airi/drizzle-duckdb-wasm':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/drizzle-duckdb-wasm
|
||||
@@ -1101,7 +1104,7 @@ importers:
|
||||
version: 2.3.0
|
||||
'@intlify/unplugin-vue-i18n':
|
||||
specifier: ^6.0.5
|
||||
version: 6.0.5(@vue/compiler-dom@3.5.13)(eslint@9.23.0(jiti@2.4.2))(rollup@4.38.0)(typescript@5.8.2)(vue-i18n@11.1.2(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))
|
||||
version: 6.0.5(@vue/compiler-dom@3.5.13)(eslint@9.23.0(jiti@2.4.2))(rollup@2.79.1)(typescript@5.8.2)(vue-i18n@11.1.2(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))
|
||||
'@proj-airi/lobe-icons':
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.4
|
||||
@@ -1140,7 +1143,7 @@ importers:
|
||||
version: 3.0.0-beta.7(typescript@5.8.2)(vue-tsc@3.0.0-alpha.2(typescript@5.8.2))(vue@3.5.13(typescript@5.8.2))
|
||||
'@vueuse/motion':
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3(magicast@0.3.5)(rollup@4.38.0)(vue@3.5.13(typescript@5.8.2))
|
||||
version: 3.0.3(magicast@0.3.5)(rollup@2.79.1)(vue@3.5.13(typescript@5.8.2))
|
||||
hfup:
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/hfup
|
||||
@@ -1152,13 +1155,13 @@ importers:
|
||||
version: 4.0.1
|
||||
unplugin-auto-import:
|
||||
specifier: ^19.1.2
|
||||
version: 19.1.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.38.0))(@vueuse/core@13.0.0(vue@3.5.13(typescript@5.8.2)))
|
||||
version: 19.1.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@2.79.1))(@vueuse/core@13.0.0(vue@3.5.13(typescript@5.8.2)))
|
||||
unplugin-vue-components:
|
||||
specifier: ^28.4.1
|
||||
version: 28.4.1(@babel/parser@7.26.10)(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.38.0))(vue@3.5.13(typescript@5.8.2))
|
||||
version: 28.4.1(@babel/parser@7.26.10)(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@2.79.1))(vue@3.5.13(typescript@5.8.2))
|
||||
unplugin-vue-macros:
|
||||
specifier: ^2.14.5
|
||||
version: 2.14.5(@vueuse/core@13.0.0(vue@3.5.13(typescript@5.8.2)))(esbuild@0.25.0)(rollup@4.38.0)(typescript@5.8.2)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue-tsc@3.0.0-alpha.2(typescript@5.8.2))(vue@3.5.13(typescript@5.8.2))
|
||||
version: 2.14.5(@vueuse/core@13.0.0(vue@3.5.13(typescript@5.8.2)))(esbuild@0.19.12)(rollup@2.79.1)(typescript@5.8.2)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue-tsc@3.0.0-alpha.2(typescript@5.8.2))(vue@3.5.13(typescript@5.8.2))
|
||||
unplugin-vue-markdown:
|
||||
specifier: ^28.3.1
|
||||
version: 28.3.1(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))
|
||||
@@ -1167,13 +1170,13 @@ importers:
|
||||
version: 0.12.0(vue-router@4.5.0(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))
|
||||
vite-bundle-visualizer:
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1(rollup@4.38.0)
|
||||
version: 1.2.1(rollup@2.79.1)
|
||||
vite-plugin-pwa:
|
||||
specifier: ^0.21.2
|
||||
version: 0.21.2(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0)
|
||||
vite-plugin-vue-devtools:
|
||||
specifier: ^7.7.2
|
||||
version: 7.7.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.38.0))(rollup@4.38.0)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.2))
|
||||
version: 7.7.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@2.79.1))(rollup@2.79.1)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.2))
|
||||
vite-plugin-vue-layouts:
|
||||
specifier: ^0.11.0
|
||||
version: 0.11.0(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(less@4.2.2)(terser@5.17.6)(tsx@4.19.3)(yaml@2.7.0))(vue-router@4.5.0(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))
|
||||
@@ -1473,12 +1476,18 @@ importers:
|
||||
'@formkit/auto-animate':
|
||||
specifier: ^0.8.2
|
||||
version: 0.8.2
|
||||
'@proj-airi/ccc':
|
||||
specifier: workspace:^
|
||||
version: link:../ccc
|
||||
'@proj-airi/server-sdk':
|
||||
specifier: workspace:^
|
||||
version: link:../server-sdk
|
||||
'@vueuse/motion':
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3(magicast@0.3.5)(rollup@4.38.0)(vue@3.5.13(typescript@5.8.2))
|
||||
radix-vue:
|
||||
specifier: ^1.9.17
|
||||
version: 1.9.17(vue@3.5.13(typescript@5.8.2))
|
||||
reka-ui:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))
|
||||
@@ -23364,7 +23373,7 @@ snapshots:
|
||||
aria-hidden: 1.2.4
|
||||
defu: 6.1.4
|
||||
fast-deep-equal: 3.1.3
|
||||
nanoid: 5.1.3
|
||||
nanoid: 5.1.5
|
||||
vue: 3.5.13(typescript@5.8.2)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
|
||||
Reference in New Issue
Block a user