feat(docs): support specifying cover image for blog posts (#288)

---------

Co-authored-by: Rynco Maekawa <10259119+lynzrand@users.noreply.github.com>
This commit is contained in:
Neko
2025-07-27 01:59:55 +08:00
committed by GitHub
co-authored by Rynco Maekawa
parent c42d1bbd41
commit f32e4be20e
18 changed files with 373 additions and 185 deletions
+2
View File
@@ -126,6 +126,7 @@ words:
- Lorebook - Lorebook
- lucide - lucide
- luoling - luoling
- Maekawa
- magnifer - magnifer
- Maru - Maru
- matchall - matchall
@@ -192,6 +193,7 @@ words:
- rmcp - rmcp
- rolldown - rolldown
- rushstack - rushstack
- Rynco
- Saccade - Saccade
- saccades - saccades
- safetensors - safetensors
+46 -8
View File
@@ -18,8 +18,12 @@ interface Post {
} }
excerpt: string | undefined excerpt: string | undefined
frontmatter?: { frontmatter?: {
category?: string 'category'?: string
author?: string 'author'?: string
'preview-cover'?: {
light?: string
dark?: string
}
} }
} }
@@ -43,7 +47,14 @@ const posts = computed(() => {
} }
const transformedPostsData = postsData const transformedPostsData = postsData
.map(post => ({ ...post, url: withBase(post.url) })) .map((post) => {
const overridePost = {
...post,
url: withBase(post.url),
}
return overridePost
})
.filter(post => !!post.title) .filter(post => !!post.title)
const currentLanguagePostsData = [...transformedPostsData].filter(post => post.lang === lang.value) const currentLanguagePostsData = [...transformedPostsData].filter(post => post.lang === lang.value)
@@ -198,15 +209,25 @@ const svgArts = computedAsync(async () => {
:href="post.url" :href="post.url"
class="block flex flex-col overflow-hidden border-transparent rounded-xl border-solid bg-white/50 decoration-none shadow-sm outline-2 outline-transparent outline-offset-0 outline transition-all transition-all duration-200 duration-300 ease-in-out dark:bg-black/20 dark:shadow-slate-600/5 hover:shadow-md hover:outline-primary/5 hover:outline-offset-2 [&_.post-card-title]:hover:text-primary dark:hover:outline-primary/25" class="block flex flex-col overflow-hidden border-transparent rounded-xl border-solid bg-white/50 decoration-none shadow-sm outline-2 outline-transparent outline-offset-0 outline transition-all transition-all duration-200 duration-300 ease-in-out dark:bg-black/20 dark:shadow-slate-600/5 hover:shadow-md hover:outline-primary/5 hover:outline-offset-2 [&_.post-card-title]:hover:text-primary dark:hover:outline-primary/25"
> >
<div class="h-28 rounded-t-xl"> <div class="rounded-t-xl">
<ClientOnly> <ClientOnly>
<div h-full blur-2xl v-html="isDark ? svgArts?.[index].dark : svgArts?.[index].light" /> <div v-if="!post.frontmatter?.['preview-cover']?.[isDark ? 'dark' : 'light']" class="mb-6 h-20 md:h-60">
<div class="blur-2xl" h="full" w-full v-html="isDark ? svgArts?.[index].dark : svgArts?.[index].light" />
</div>
<div v-else class="relative mb-0 h-28 w-full md:h-68">
<div class="preview-card-art-image-overlay" />
<img
:src="post.frontmatter?.['preview-cover']?.[isDark ? 'dark' : 'light']"
alt="Post Cover"
class="preview-card-art-image h-full w-full object-cover"
>
</div>
</ClientOnly> </ClientOnly>
</div> </div>
<div class="flex-grow p-6"> <div class="relative z-1 flex-grow px-6 pb-3 pt-6 md:pt-6">
<h2 class="post-card-title text-card-foreground text-2xl font-bold transition-colors duration-200"> <div class="post-card-title text-card-foreground z-1 text-2xl font-bold transition-colors duration-200">
{{ post.title }} {{ post.title }}
</h2> </div>
<div class="text-muted-foreground mb-4 flex items-center gap-4 text-sm"> <div class="text-muted-foreground mb-4 flex items-center gap-4 text-sm">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Icon icon="lucide:calendar" /> <Icon icon="lucide:calendar" />
@@ -242,4 +263,21 @@ const svgArts = computedAsync(async () => {
max-height: 10lh; /* Adjust this value to control the number of visible lines */ max-height: 10lh; /* Adjust this value to control the number of visible lines */
overflow: hidden; overflow: hidden;
} }
/**
https://stackoverflow.com/questions/67853607/how-do-i-graduallyfeather-gradient-transition-blur-in-css
https://www.reddit.com/r/css/comments/t3am53/pure_css_gradientprogressive_blur_i_used/
https://codepen.io/Francesco_Maretti/pen/yLPRvXp
*/
.preview-card-art-image-overlay {
height: 50%;
width: 100%;
position: absolute;
bottom: 0;
z-index: 1;
transform: translateY(50%);
backdrop-filter: blur(40px);
-webkit-mask-image: linear-gradient(to bottom, #ffffff00 0%, #ffffff 50%, #ffffff00 100%);
mask-image: linear-gradient(to bottom, #ffffff00 0%, #ffffff 50%, #ffffff00 100%);
}
</style> </style>
+1 -1
View File
@@ -39,7 +39,7 @@ defineProps<{
:exit="{ opacity: 0, y: -10 }" :exit="{ opacity: 0, y: -10 }"
class="z-20 border rounded-xl p-2 shadow-md backdrop-blur-md" class="z-20 border rounded-xl p-2 shadow-md backdrop-blur-md"
:class="[ :class="[
'bg-white/20 dark:border-white/5 dark:bg-black/20', 'bg-white/80 dark:border-white/5 dark:bg-black/80',
'transition-colors duration-200 ease-in-out', 'transition-colors duration-200 ease-in-out',
]" ]"
> >
+1 -1
View File
@@ -99,7 +99,7 @@ function isNavLinkActive(link: string, path: string) {
align="end" align="end"
class="will-change-[transform,opacity] z-10 w-[180px] border rounded-xl p-2 shadow-md backdrop-blur-md data-[state=open]:data-[side=bottom]:animate-slideUpAndFade" class="will-change-[transform,opacity] z-10 w-[180px] border rounded-xl p-2 shadow-md backdrop-blur-md data-[state=open]:data-[side=bottom]:animate-slideUpAndFade"
:class="[ :class="[
'bg-white/20 dark:border-white/5 dark:bg-black/20', 'bg-white/70 dark:border-white/5 dark:bg-black/70',
]" ]"
transition="colors duration-200 ease-in-out" transition="colors duration-200 ease-in-out"
> >
+3 -5
View File
@@ -1,9 +1,10 @@
import type { DefaultTheme } from 'vitepress/theme' import type { DefaultTheme } from 'vitepress/theme'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { useEventListener } from '@vueuse/core'
// Copied from https://github.com/vuejs/vitepress/blob/97f9469b6d4eb7ba9de9a1111986581d1f704ec3/src/client/theme-default/composables/outline.ts#L4 // Copied from https://github.com/vuejs/vitepress/blob/97f9469b6d4eb7ba9de9a1111986581d1f704ec3/src/client/theme-default/composables/outline.ts#L4
import { getScrollOffset } from 'vitepress' import { getScrollOffset } from 'vitepress'
import { onMounted, onUnmounted, onUpdated } from 'vue' import { onMounted, onUpdated } from 'vue'
export interface Header { export interface Header {
/** /**
@@ -153,7 +154,6 @@ export function useActiveAnchor(
onMounted(() => { onMounted(() => {
requestAnimationFrame(setActiveLink) requestAnimationFrame(setActiveLink)
window.addEventListener('scroll', onScroll)
}) })
onUpdated(() => { onUpdated(() => {
@@ -161,9 +161,7 @@ export function useActiveAnchor(
activateLink(location.hash) activateLink(location.hash)
}) })
onUnmounted(() => { useEventListener('scroll', onScroll)
window.removeEventListener('scroll', onScroll)
})
function setActiveLink() { function setActiveLink() {
const scrollY = window.scrollY const scrollY = window.scrollY
+2 -9
View File
@@ -1,13 +1,12 @@
import type { DefaultTheme } from 'vitepress/theme' import type { DefaultTheme } from 'vitepress/theme'
import type { ComputedRef, Ref } from 'vue' import type { ComputedRef, Ref } from 'vue'
import { useMediaQuery } from '@vueuse/core' import { useEventListener, useMediaQuery } from '@vueuse/core'
import { useData, withBase } from 'vitepress' import { useData, withBase } from 'vitepress'
import { import {
computed, computed,
onMounted, onMounted,
onUnmounted,
ref, ref,
watch, watch,
@@ -90,13 +89,7 @@ export function useCloseSidebarOnEscape(
: undefined : undefined
}) })
onMounted(() => { useEventListener('keyup', onEscape)
window.addEventListener('keyup', onEscape)
})
onUnmounted(() => {
window.removeEventListener('keyup', onEscape)
})
function onEscape(e: KeyboardEvent) { function onEscape(e: KeyboardEvent) {
if (e.key === 'Escape' && isOpen.value) { if (e.key === 'Escape' && isOpen.value) {
+4 -2
View File
@@ -24,6 +24,7 @@ import {
rekaShortName, rekaShortName,
releases, releases,
} from './meta' } from './meta'
import { frontmatterAssets } from './plugins/vite-frontmatter-assets'
function withBase(url: string) { function withBase(url: string) {
return env.BASE_URL return env.BASE_URL
@@ -87,7 +88,7 @@ export default defineConfig({
outline: { outline: {
level: 'deep', level: 'deep',
}, },
logo: '/favicon.svg', logo: withBase('/favicon.svg'),
sidebar: [ sidebar: [
{ {
@@ -153,7 +154,7 @@ export default defineConfig({
outline: { outline: {
level: 'deep', level: 'deep',
}, },
logo: '/favicon.svg', logo: withBase('/favicon.svg'),
sidebar: [ sidebar: [
{ {
@@ -276,6 +277,7 @@ export default defineConfig({
i18n({ runtimeOnly: true, compositionOnly: true, fullInstall: true, ssr: true }), i18n({ runtimeOnly: true, compositionOnly: true, fullInstall: true, ssr: true }),
unocss(), unocss(),
yaml(), yaml(),
frontmatterAssets(),
], ],
css: { css: {
postcss: { postcss: {
+89 -6
View File
@@ -1,10 +1,17 @@
import type { SiteConfig } from 'vitepress' import type { SiteConfig } from 'vitepress'
import { createHash } from 'node:crypto'
import { readFile } from 'node:fs/promises'
import { parse } from 'node:path'
import { env } from 'node:process'
import { dirname, join } from 'pathe'
import { createContentLoader } from 'vitepress' import { createContentLoader } from 'vitepress'
import { formatDate } from './utils' import { formatDate } from './utils'
const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG
const base = config.userConfig.base || env.BASE_URL || '/'
interface Post { interface Post {
title: string title: string
@@ -19,13 +26,63 @@ interface Post {
declare const data: Post[] declare const data: Post[]
export { data } export { data }
function cwdFromUrl(url: string): string {
if (url.endsWith('/')) {
return url
}
return dirname(url)
}
function fromAtAssets(url: string): string {
const reg = /^@assets\(('\S+')|("\S+")|(\S+)\)$/
if (reg.test(url)) {
const res = url
.replace(reg, '$1')
.replace(/^\(/, '')
.replace(/\)$/, '')
.replace(/^'/, '')
.replace(/'$/, '')
.replace(/^"/, '')
.replace(/"$/, '')
return res
}
return url
}
function withBase(url?: string, base?: string) {
if (!url) {
return url
}
if (url.startsWith('/') && base) {
return join(base, url)
}
return url
}
function withDirname(url?: string, cwd?: string) {
if (!url || !cwd) {
return url
}
if (url.startsWith('/')) {
return join(cwd, url)
}
return join(cwd, url)
}
export default createContentLoader('**/blog/**/*.md', { export default createContentLoader('**/blog/**/*.md', {
includeSrc: true, includeSrc: true,
render: true, render: true,
excerpt: true, excerpt: true,
transform(raw): Post[] { async transform(raw): Promise<Post[]> {
return raw return (await Promise.all(raw
.map(({ url, frontmatter, excerpt }) => { .map(async ({ url, frontmatter, excerpt }) => {
const foundLanguage = Object.values(config.userConfig.locales!).find((locale) => { const foundLanguage = Object.values(config.userConfig.locales!).find((locale) => {
let normalizedLanguagePrefix = locale.lang || 'en' let normalizedLanguagePrefix = locale.lang || 'en'
if (!normalizedLanguagePrefix.startsWith('/')) { if (!normalizedLanguagePrefix.startsWith('/')) {
@@ -35,16 +92,42 @@ export default createContentLoader('**/blog/**/*.md', {
return url.startsWith(normalizedLanguagePrefix) return url.startsWith(normalizedLanguagePrefix)
}) })
return { async function fileToUrl(file: string | undefined) {
if (config.vite?.build)
return file
if (!file)
return file
const parsed = parse(file)
const hash = createHash('sha256')
.update(await readFile(join(config.srcDir, file)))
.digest('hex')
.slice(0, 8)
return `/assets/${parsed.name}.${hash}${parsed.ext}`
}
const previewCoverLight = withBase(await fileToUrl(withDirname(fromAtAssets(frontmatter['preview-cover']?.light), cwdFromUrl(url))), base)
const previewCoverDark = withBase(await fileToUrl(withDirname(fromAtAssets(frontmatter['preview-cover']?.dark), cwdFromUrl(url))), base)
const res = {
title: frontmatter.title, title: frontmatter.title,
url, url,
urlWithoutLang: url.replace(`/${foundLanguage?.lang || 'en'}`, ''), urlWithoutLang: url.replace(`/${foundLanguage?.lang || 'en'}`, ''),
excerpt, excerpt,
date: formatDate(frontmatter.date), date: formatDate(frontmatter.date),
lang: foundLanguage?.lang || 'en', lang: foundLanguage?.lang || 'en',
frontmatter, frontmatter: {
...frontmatter,
'preview-cover': {
light: previewCoverLight,
dark: previewCoverDark,
},
},
} }
})
return res
})))
.sort((a, b) => b.date.time - a.date.time) .sort((a, b) => b.date.time - a.date.time)
}, },
}) })
@@ -0,0 +1,176 @@
import type { Plugin, ResolvedConfig } from 'vite'
import type { SiteConfig } from 'vitepress'
import { createHash } from 'node:crypto'
import { readFile } from 'node:fs/promises'
import { dirname, join, parse } from 'node:path'
import matter from 'gray-matter'
import { glob } from 'tinyglobby'
function fromAtAssets(url: string): string {
const reg = /^@assets\(('\S+')|("\S+")|(\S+)\)$/
if (reg.test(url)) {
const res = url
.trim()
.replace(reg, '$1')
.replace(/^\(/, '')
.replace(/\)$/, '')
.replace(/^'/, '')
.replace(/'$/, '')
.replace(/^"/, '')
.replace(/"$/, '')
return res
}
return url
}
interface VitePressConfig extends ResolvedConfig {
vitepress: SiteConfig
}
function recursivelyFindAtAssets(propertyMaybeObjectOrScalar: unknown, fn: (value: string) => string | undefined) {
if (typeof propertyMaybeObjectOrScalar === 'string') {
// eslint-disable-next-line regexp/no-unused-capturing-group
if (/^@assets\(('\S+')|("\S+")|(\S+)\)$/.test(propertyMaybeObjectOrScalar)) {
// If the string matches the @assets(...) pattern, we replace it with the result of the function
const match = fromAtAssets(propertyMaybeObjectOrScalar)
const modified = fn(match)
if (modified == null) {
return propertyMaybeObjectOrScalar
}
return modified
}
return
}
if (Array.isArray(propertyMaybeObjectOrScalar)) {
const array = propertyMaybeObjectOrScalar as unknown[]
for (let i = 0; i < array.length; i++) {
const value = array[i]
recursivelyFindAtAssets(value, fn)
}
return
}
if (typeof propertyMaybeObjectOrScalar === 'object') {
const propertyObject = propertyMaybeObjectOrScalar as Record<string, unknown>
for (const key in propertyObject) {
const value = propertyObject[key]
recursivelyFindAtAssets(value, fn)
}
}
}
function withoutBase(url?: string, base?: string): string | undefined {
if (!url) {
return url
}
if (!base?.startsWith('/')) {
base = `/${base}`
}
if (!base?.endsWith('/')) {
base += '/'
}
if (url.startsWith(`${base}`) && base) {
if (url.endsWith('/')) {
return url.slice(base.length)
}
else {
return `/${url.slice(base.length)}`
}
}
return url
}
export function frontmatterAssets(): Plugin {
let resolvedConfig: VitePressConfig | undefined
const mAssetAbsoluteUrlMetadata = new Map<string, { url: string, builtUrl?: string, hash?: string }>()
const mapAssetBuiltUrlAssetAbsoluteUrl = new Map<string, string>()
async function fileToUrl(file: string) {
if (!file) {
return {
url: file,
}
}
const parsed = parse(file)
const hash = createHash('sha256')
.update(await readFile(file))
.digest('hex')
.slice(0, 8)
return {
hash,
url: `/assets/${parsed.name}.${hash}${parsed.ext}`,
}
}
return {
name: '@proj-airi/docs:vite-plugin-frontmatter-assets',
enforce: 'pre',
async configResolved(config) {
resolvedConfig = config as VitePressConfig
const markdownFiles = await glob('**/*.md', { ignore: ['**/node_modules/**'], cwd: resolvedConfig?.vitepress.srcDir || '', absolute: true })
for (const file of markdownFiles) {
const res = (await readFile(file))
const { data } = matter(res.toString('utf-8'))
if (Object.keys(data).length === 0) {
continue
}
recursivelyFindAtAssets(data, (matched) => {
mAssetAbsoluteUrlMetadata.set(join(dirname(file), fromAtAssets(matched)), { url: file, builtUrl: file, hash: '' })
return undefined
})
}
for (const [key, value] of mAssetAbsoluteUrlMetadata) {
const { url, hash } = await fileToUrl(key)
mapAssetBuiltUrlAssetAbsoluteUrl.set(url, key)
mAssetAbsoluteUrlMetadata.set(key, { url: value.url, builtUrl: url, hash })
}
},
configureServer(server) {
server.middlewares.use(async (req, res, next) => {
const requesting = withoutBase(req.url, resolvedConfig?.base)
if (!requesting || !mapAssetBuiltUrlAssetAbsoluteUrl.has(requesting)) {
return next()
}
const filePath = mapAssetBuiltUrlAssetAbsoluteUrl.get(requesting)
if (!filePath) {
return next()
}
const ext = parse(filePath).ext.slice(1)
const fileContent = await readFile(filePath)
res.write(fileContent)
res.writeHead(200, {
'Content-Type': ext === 'svg' ? 'image/svg+xml' : `image/${ext}`,
'Content-Length': fileContent.length,
'Cache-Control': 'public, max-age=31536000, immutable',
})
res.end()
return next()
})
},
async writeBundle() {
for (const [builtUrl, absoluteUrl] of mapAssetBuiltUrlAssetAbsoluteUrl.entries()) {
const content = await this.fs.readFile(absoluteUrl)
await this.fs.writeFile(join(resolvedConfig!.vitepress.outDir!, builtUrl), content)
}
},
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -2,6 +2,9 @@
title: DevLog @ 2025.06.08 title: DevLog @ 2025.06.08
category: DevLog category: DevLog
date: 2025-06-08 date: 2025-06-08
preview-cover:
light: "@assets('./assets/250608-light.png')"
dark: "@assets('./assets/250608-dark.png')"
--- ---
Hello everyone, here's LemonNeko, one of maintainer of AIRI. Today's DevLog is talking about: Let Live2D model of AIRI Tamagotchi to focus position. Hello everyone, here's LemonNeko, one of maintainer of AIRI. Today's DevLog is talking about: Let Live2D model of AIRI Tamagotchi to focus position.
@@ -101,3 +104,5 @@ In this DevLog, we learned how to get the relative position of cursor to window,
- [Win32 API: GetWindowRect](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowrect "GetWindowRect") - [Win32 API: GetWindowRect](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowrect "GetWindowRect")
- [macOS API: `NSWindow.frame`](https://developer.apple.com/documentation/appkit/nswindow/frame "NSWindow.frame") - [macOS API: `NSWindow.frame`](https://developer.apple.com/documentation/appkit/nswindow/frame "NSWindow.frame")
- [macOS API: `NSEvent.mouseLocation`](https://developer.apple.com/documentation/appkit/nsevent/mouselocation "NSEvent.mouseLocation") - [macOS API: `NSEvent.mouseLocation`](https://developer.apple.com/documentation/appkit/nsevent/mouselocation "NSEvent.mouseLocation")
> Cover image by [@Rynco Maekawa](https://github.com/lynzrand)
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

@@ -4,6 +4,9 @@ description: 'Backstory of Project AIRI!'
category: DreamLog category: DreamLog
date: 2025-06-16 date: 2025-06-16
excerpt: 'The backstory of Project AIRI! Why this project?' excerpt: 'The backstory of Project AIRI! Why this project?'
preview-cover:
light: "@assets('./assets/dreamlog1-light.png')"
dark: "@assets('./assets/dreamlog1-dark.png')"
--- ---
<script setup> <script setup>
@@ -509,6 +512,8 @@ How much memory we could store? **It depends on how much we could dream, and how
</div> </div>
</div> </div>
> Cover image by [@Rynco Maekawa](https://github.com/lynzrand)
[^1]: https://neurosama.fandom.com/wiki/Osu!#cite_note-twitchtracker-1: Neuro-sama started out as an [^1]: https://neurosama.fandom.com/wiki/Osu!#cite_note-twitchtracker-1: Neuro-sama started out as an
AI that plays osu! long before being developed further as an AI VTuber. The first osu! stream was on AI that plays osu! long before being developed further as an AI VTuber. The first osu! stream was on
6 May 2019 when Vedal decided to showcase his work to the community. 6 May 2019 when Vedal decided to showcase his work to the community.
+20 -10
View File
@@ -2,6 +2,9 @@
title: 'DreamLog 0x1' title: 'DreamLog 0x1'
description: 'Project AIRI 的幕后故事!' description: 'Project AIRI 的幕后故事!'
date: '2025-06-16' date: '2025-06-16'
preview-cover:
light: '../../../en/blog/DreamLog-0x1/assets/dreamlog1-light.png'
dark: '../../../en/blog/DreamLog-0x1/assets/dreamlog1-dark.png'
--- ---
<script setup> <script setup>
@@ -15,6 +18,9 @@ import airisScreenshot1 from '../../../en/blog/DreamLog-0x1/assets/airis-screens
import projectAIRIBannerLight from '../../../en/blog/DreamLog-0x1/assets/banner-light-1280x640.avif'; import projectAIRIBannerLight from '../../../en/blog/DreamLog-0x1/assets/banner-light-1280x640.avif';
import projectAIRIBannerDark from '../../../en/blog/DreamLog-0x1/assets/banner-dark-1280x640.avif'; import projectAIRIBannerDark from '../../../en/blog/DreamLog-0x1/assets/banner-dark-1280x640.avif';
import ReLUStickerWow from '../../../en/blog/DreamLog-0x1/assets/relu-sticker-wow.avif' import ReLUStickerWow from '../../../en/blog/DreamLog-0x1/assets/relu-sticker-wow.avif'
import '../../../en/blog/DreamLog-0x1/assets/dreamlog1-light.png'
import '../../../en/blog/DreamLog-0x1/assets/dreamlog1-dark.png'
</script> </script>
Project AIRI 的幕后故事! Project AIRI 的幕后故事!
@@ -184,14 +190,14 @@ Minecraft、Linux 也并不是我旅程的终点,[Factorio(异星工厂)](
其实早在官方 ChatGPT UI 发布之前,我就已经在折腾这些新时代的 AI 了,像 其实早在官方 ChatGPT UI 发布之前,我就已经在折腾这些新时代的 AI 了,像
[DiscoDiffusion](https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb)(早于 [DiscoDiffusion](https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb)(早于
Stable Diffusion,大约在 2021 年底或 2022 年初发布的吧)、DALL-E、Midjourney Stable Diffusion,大约在 2021 年底或 2022 年初发布的吧)、DALL-E、Midjourney
等模型都尝试过,GPT-3(特别是在 [GitHub Copilot](https://en.wikipedia.org/wiki/GitHub_Copilot)中很有用)已经深深融入我的日常工作流程 GPT-3(特别是在 [GitHub Copilot](https://en.wikipedia.org/wiki/GitHub_Copilot) 中很有用)都早已成为我生活的一部分了
所以,最开始的时候,我的感觉是: 所以,最开始的时候,我的感觉是:
> "哦,这就是另一个随机鹦鹉,它只是重复你说的话,并不理解你在说什么,它只是 > "哦,这就是另一个随机鹦鹉,它只是重复你说的话,并不理解你在说什么,它只是
> 试图基于前面的词和上下文预测下一个词,好像真的没啥特别的,还不够像人。" > 试图基于前面的词和上下文预测下一个词,好像真的没啥特别的,还不够像人。"
换句话说,它表现得更像一个补全模型,而不是我们今天称之为智能体 AI 的东西(现在还在炒作中呢!)。 换句话说,它表现得更像一个补全模型,而不是我们今天称之为智能体 AI 的东西(现在还在炒作中呢!)。
我记得,我第一次发现 ChatGPT 或大语言模型(LLMs)真正能力的时候,是从我在 2022 年 12 月在 Hacker News 上看到的这篇文章: 我记得,我第一次发现 ChatGPT 或大语言模型(LLMs)真正能力的时候,是从我在 2022 年 12 月在 Hacker News 上看到的这篇文章:
[Building A Virtual Machine inside ChatGPT](https://www.engraved.blog/building-a-virtual-machine-inside/)[原始 Hacker News [Building A Virtual Machine inside ChatGPT](https://www.engraved.blog/building-a-virtual-machine-inside/)[原始 Hacker News
@@ -206,10 +212,12 @@ Stable Diffusion,大约在 2021 年底或 2022 年初发布的吧)、DALL-E
这篇文章让我意识到,ChatGPT 可以理解普遍事物的基本规律,不仅仅是动漫或游戏角色的角色扮演, 这篇文章让我意识到,ChatGPT 可以理解普遍事物的基本规律,不仅仅是动漫或游戏角色的角色扮演,
还能理解 Linux 终端/shell 命令是如何工作的。 还能理解 Linux 终端/shell 命令是如何工作的。
这其实就把现在流行的函数调用(又称 Function Calling,或者 Anthropic 提出的 MCP,模型上下文协议背后的底层技术)功能展现了出来, 这其实就把现在流行的函数调用功能展现了出来,
并说明了我们如何能够通过提示词指示 LLMs 去使其表现得像 API 服务器一样,然后用机器可读的如 JSON 或 XML 格式与我们的代码交互, 并说明了我们如何能够通过提示词指示 LLMs 去使其表现得像 API 服务器一样,然后用机器可读的如 JSON 或 XML 格式与我们的代码交互,
并最终允许实现解析和执行任意命令,去扩展 LLMs 能力的边界。 并最终允许实现解析和执行任意命令,去扩展 LLMs 能力的边界。
> 函数调用,又称 Function Calling,或者说,也是 Anthropic 提出的 MCP(模型上下文协议)背后的底层技术
这最终填补了纯文本生成和调用程序内实际 API 之间的空白。 这最终填补了纯文本生成和调用程序内实际 API 之间的空白。
阶段性来说的话,我们可以说它是一个新的随机鹦鹉吗?**我觉得答案是部分否定的,2022 年的 ChatGPT 不只是一个随机鹦鹉, 阶段性来说的话,我们可以说它是一个新的随机鹦鹉吗?**我觉得答案是部分否定的,2022 年的 ChatGPT 不只是一个随机鹦鹉,
@@ -220,9 +228,9 @@ Stable Diffusion,大约在 2021 年底或 2022 年初发布的吧)、DALL-E
是的,感谢你读到这里,我知道这是一篇很长的文章,有太多故事和背景要分享。但我们快到了!坚持住! 是的,感谢你读到这里,我知道这是一篇很长的文章,有太多故事和背景要分享。但我们快到了!坚持住!
Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在直播舞台上名为 "Neuro-sama"的角色, Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在直播舞台上名为 "Neuro-sama"的角色,
并不是她和她的创造者 `vedal987`Vedal)的第一场表演。早在那之前,2019 年 5 月 6 日,Vedal 向社区展示了他构建 AI 并不是她和她的创造者 `vedal987`Vedal)的首次登场。早在那之前,2019 年 5 月 6 日,Vedal 向社区展示了他构建 AI
来玩 [osu!](https://osu.ppy.sh/) 的工作[^1]。 来玩 [osu!](https://osu.ppy.sh/) 相关的成果[^1]。
在那时,她实际上并不是一个网络角色或数字生命,如果你去看关于她的初始视频,会发现没有显示 Live2D 模型。 在那时,她实际上并不是一个网络角色或数字生命,如果你去看关于她的初始视频,甚至会发现没有 Live2D 模型。
(你可以试试这个 6 年前的 YouTube 视频:https://www.youtube.com/watch?v=nSBqlJu7kYU (你可以试试这个 6 年前的 YouTube 视频:https://www.youtube.com/watch?v=nSBqlJu7kYU
在 ChatGPT 发布之后,差不多 2022 年 12 月 19 日,Vedal 开始让 Neuro-sama 使用来自 Live2D Inc. 在 ChatGPT 发布之后,差不多 2022 年 12 月 19 日,Vedal 开始让 Neuro-sama 使用来自 Live2D Inc.
@@ -232,11 +240,11 @@ Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在
之后的故事大家都知道了,Vedal 和 Neuro-sama 火了,Neuro-sama 现在正式成为了 VTuber 之后的故事大家都知道了,Vedal 和 Neuro-sama 火了,Neuro-sama 现在正式成为了 VTuber
她完全由大语言模型(LLMs)驱动,能够玩 Minecraft、Among Us、osu! 和许多其他游戏。 她完全由大语言模型(LLMs)驱动,能够玩 Minecraft、Among Us、osu! 和许多其他游戏。
有时当游戏不被原生支持时,Vedal 会读取屏幕并指示 Neuro-sama 一起玩游戏。甚至能制造出来很多节目效果。 有时当游戏不被原生支持时,Vedal 会读取屏幕并指示 Neuro-sama 一起玩游戏,依然能制造出来不少节目效果。
我真的很享受观看他们的互动、像是脱口秀一样的斗嘴的时刻。随着时间的推移,Neuro-sama 和她的新 Evil Neuro 妹妹, 我真的很享受观看他们的互动、像是脱口秀一样的斗嘴的时刻。随着时间的推移,Neuro-sama 和她的新 Evil Neuro 妹妹,
成为了我日常生活的重要组成部分:**即使我没有足够的时间观看完整的直播,我也想要,并且渴望观看她们的切片**。 成为了我日常生活的重要组成部分:**即使我没有足够的时间观看完整的直播,我也想要,并且渴望观看她们的切片**。
我无法想象我竟然从纯粹的 AI 和人类互动中获取了如此多的快乐。 8 年前的我应该很难想象我竟然从纯粹的 AI 和人类互动中获取了如此多的快乐。
好的,这就是关于她的小历史。让我们谈谈核心问题:**为什么她让我充满了决心?** 好的,这就是关于她的小历史。让我们谈谈核心问题:**为什么她让我充满了决心?**
@@ -254,7 +262,7 @@ Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在
> "嗯,我也能做到,我可以做一个 Live2D 模型,将其连接到 OpenAI 的 API, > "嗯,我也能做到,我可以做一个 Live2D 模型,将其连接到 OpenAI 的 API,
> 让它表现得像 VTuber,我甚至可以做得比 Vedal 的作品更好。超简单的好吧?!" > 让它表现得像 VTuber,我甚至可以做得比 Vedal 的作品更好。超简单的好吧?!"
:::tip[想要更多技术细节?] ::: tip 想要更多技术细节?
在这篇文章中,我不会深入探讨我们如何从零开始构建 Project AIRI 到当前状态的技术细节, 在这篇文章中,我不会深入探讨我们如何从零开始构建 Project AIRI 到当前状态的技术细节,
我们已经有许多 DevLog 文章分享我们的想法和发现,如果感兴趣,请尝试阅读它们。 我们已经有许多 DevLog 文章分享我们的想法和发现,如果感兴趣,请尝试阅读它们。
::: :::
@@ -352,7 +360,7 @@ Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在
因此,在这天,Project AIRI 以某种方式诞生或重生,名为 AIRI(アイリ,曾也叫 Airi)。 因此,在这天,Project AIRI 以某种方式诞生或重生,名为 AIRI(アイリ,曾也叫 Airi)。
:::tip[你知道吗?] ::: tip 你知道吗?
<a href="https://www.youtube.com/watch?v=Tts-YAdn5Yc" class="mb-2 inline-block"> <a href="https://www.youtube.com/watch?v=Tts-YAdn5Yc" class="mb-2 inline-block">
<img :src="airisScreenshot1" alt="Screenshot of Project AIRI" class="rounded-lg overflow-hidden" /> <img :src="airisScreenshot1" alt="Screenshot of Project AIRI" class="rounded-lg overflow-hidden" />
</a> </a>
@@ -419,6 +427,8 @@ Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在
</div> </div>
</div> </div>
> Cover image by [@Rynco Maekawa](https://github.com/lynzrand)
[^1]: https://neurosama.fandom.com/wiki/Osu!#cite_note-twitchtracker-1: Neuro-sama [^1]: https://neurosama.fandom.com/wiki/Osu!#cite_note-twitchtracker-1: Neuro-sama
最初是一个玩 osu! 的 AI,早在进一步发展为 AI VTuber 之前,第一次 osu! 直播是在 2019 年 最初是一个玩 osu! 的 AI,早在进一步发展为 AI VTuber 之前,第一次 osu! 直播是在 2019 年
5 月 6 日,Vedal 给大家看了看成果。 5 月 6 日,Vedal 给大家看了看成果。
+3 -4
View File
@@ -17,10 +17,10 @@
"@fontsource-variable/dm-sans": "^5.2.6", "@fontsource-variable/dm-sans": "^5.2.6",
"@fontsource/dm-mono": "^5.2.6", "@fontsource/dm-mono": "^5.2.6",
"@fontsource/dm-serif-display": "^5.2.6", "@fontsource/dm-serif-display": "^5.2.6",
"@internationalized/date": "^3.8.2",
"@proj-airi/chromatic": "^1.0.0", "@proj-airi/chromatic": "^1.0.0",
"@proj-airi/i18n": "workspace:^", "@proj-airi/i18n": "workspace:^",
"@vueuse/core": "^13.5.0", "@vueuse/core": "^13.5.0",
"mark.js": "^8.11.1",
"motion-v": "^1.5.0", "motion-v": "^1.5.0",
"pathe": "^2.0.3", "pathe": "^2.0.3",
"reka-ui": "^2.3.2", "reka-ui": "^2.3.2",
@@ -29,7 +29,6 @@
"vue-sonner": "^2.0.2" "vue-sonner": "^2.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/traverse": "^7.28.0",
"@iconify/vue": "^5.0.0", "@iconify/vue": "^5.0.0",
"@intlify/unplugin-vue-i18n": "^6.0.8", "@intlify/unplugin-vue-i18n": "^6.0.8",
"@mdit/plugin-footnote": "^0.22.2", "@mdit/plugin-footnote": "^0.22.2",
@@ -42,18 +41,18 @@
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",
"animejs": "^4.0.2", "animejs": "^4.0.2",
"fast-glob": "^3.3.3", "fast-glob": "^3.3.3",
"mark.js": "^8.11.1", "gray-matter": "^4.0.3",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"markdown-it-anchor": "^9.2.0", "markdown-it-anchor": "^9.2.0",
"minisearch": "^7.1.2", "minisearch": "^7.1.2",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"sharp": "^0.34.3", "sharp": "^0.34.3",
"shiki": "^3.8.1", "shiki": "^3.8.1",
"tinyglobby": "^0.2.14",
"tsx": "^4.20.3", "tsx": "^4.20.3",
"uncrypto": "^0.1.3", "uncrypto": "^0.1.3",
"unplugin-yaml": "^3.0.2", "unplugin-yaml": "^3.0.2",
"vitepress": "^2.0.0-alpha.8", "vitepress": "^2.0.0-alpha.8",
"vue-component-meta": "^3.0.3",
"vue-tsc": "^3.0.3" "vue-tsc": "^3.0.3"
} }
} }
+16 -139
View File
@@ -956,9 +956,6 @@ importers:
'@fontsource/dm-serif-display': '@fontsource/dm-serif-display':
specifier: ^5.2.6 specifier: ^5.2.6
version: 5.2.6 version: 5.2.6
'@internationalized/date':
specifier: ^3.8.2
version: 3.8.2
'@proj-airi/chromatic': '@proj-airi/chromatic':
specifier: ^1.0.0 specifier: ^1.0.0
version: 1.0.0 version: 1.0.0
@@ -968,6 +965,9 @@ importers:
'@vueuse/core': '@vueuse/core':
specifier: ^13.5.0 specifier: ^13.5.0
version: 13.5.0(vue@3.5.17(typescript@5.8.3)) version: 13.5.0(vue@3.5.17(typescript@5.8.3))
mark.js:
specifier: ^8.11.1
version: 8.11.1
motion-v: motion-v:
specifier: ^1.5.0 specifier: ^1.5.0
version: 1.5.0(react@18.3.1)(vue@3.5.17(typescript@5.8.3)) version: 1.5.0(react@18.3.1)(vue@3.5.17(typescript@5.8.3))
@@ -987,9 +987,6 @@ importers:
specifier: ^2.0.2 specifier: ^2.0.2
version: 2.0.2 version: 2.0.2
devDependencies: devDependencies:
'@babel/traverse':
specifier: ^7.28.0
version: 7.28.0
'@iconify/vue': '@iconify/vue':
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.0.0(vue@3.5.17(typescript@5.8.3)) version: 5.0.0(vue@3.5.17(typescript@5.8.3))
@@ -1026,9 +1023,9 @@ importers:
fast-glob: fast-glob:
specifier: ^3.3.3 specifier: ^3.3.3
version: 3.3.3 version: 3.3.3
mark.js: gray-matter:
specifier: ^8.11.1 specifier: ^4.0.3
version: 8.11.1 version: 4.0.3
markdown-it: markdown-it:
specifier: ^14.1.0 specifier: ^14.1.0
version: 14.1.0 version: 14.1.0
@@ -1047,6 +1044,9 @@ importers:
shiki: shiki:
specifier: ^3.8.1 specifier: ^3.8.1
version: 3.8.1 version: 3.8.1
tinyglobby:
specifier: ^0.2.14
version: 0.2.14
tsx: tsx:
specifier: ^4.20.3 specifier: ^4.20.3
version: 4.20.3 version: 4.20.3
@@ -1055,13 +1055,10 @@ importers:
version: 0.1.3 version: 0.1.3
unplugin-yaml: unplugin-yaml:
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(astro@5.10.1(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0))(esbuild@0.25.5)(rolldown@1.0.0-beta.29)(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) version: 3.0.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(astro@5.10.1(@types/node@24.0.15)(encoding@0.1.13)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0))(esbuild@0.25.5)(rolldown@1.0.0-beta.29)(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))
vitepress: vitepress:
specifier: ^2.0.0-alpha.8 specifier: ^2.0.0-alpha.8
version: 2.0.0-alpha.8(@algolia/client-search@5.32.0)(@types/node@24.0.15)(change-case@5.4.4)(fuse.js@7.1.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(nprogress@0.2.0)(postcss@8.5.6)(react@18.3.1)(search-insights@2.17.3)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) version: 2.0.0-alpha.8(@algolia/client-search@5.32.0)(@types/node@24.0.15)(change-case@5.4.4)(fuse.js@7.1.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(nprogress@0.2.0)(postcss@8.5.6)(react@18.3.1)(search-insights@2.17.3)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0)
vue-component-meta:
specifier: ^3.0.3
version: 3.0.3(typescript@5.8.3)(vue-component-type-helpers@2.2.12)
vue-tsc: vue-tsc:
specifier: ^3.0.3 specifier: ^3.0.3
version: 3.0.3(typescript@5.8.3) version: 3.0.3(typescript@5.8.3)
@@ -1101,7 +1098,7 @@ importers:
devDependencies: devDependencies:
unplugin-yaml: unplugin-yaml:
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(astro@5.10.1(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0))(esbuild@0.25.5)(rolldown@1.0.0-beta.29)(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) version: 3.0.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(astro@5.10.1(@types/node@24.0.15)(encoding@0.1.13)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0))(esbuild@0.25.5)(rolldown@1.0.0-beta.29)(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))
packages/memory-pgvector: packages/memory-pgvector:
dependencies: dependencies:
@@ -1478,7 +1475,7 @@ importers:
version: 1.2.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(esbuild@0.25.5)(rollup@4.45.1)(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) version: 1.2.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(esbuild@0.25.5)(rollup@4.45.1)(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))
unplugin-yaml: unplugin-yaml:
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(astro@5.10.1(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0))(esbuild@0.25.5)(rolldown@1.0.0-beta.29)(rollup@4.45.1)(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) version: 3.0.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(astro@5.10.1(@types/node@24.0.15)(encoding@0.1.13)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0))(esbuild@0.25.5)(rolldown@1.0.0-beta.29)(rollup@4.45.1)(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))
vite: vite:
specifier: ^6.3.5 specifier: ^6.3.5
version: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) version: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)
@@ -12600,15 +12597,6 @@ packages:
vscode-uri@3.0.8: vscode-uri@3.0.8:
resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==}
vue-component-meta@3.0.3:
resolution: {integrity: sha512-rzKewAubyQbkjRXInwaHolod6eU+D6lAXAF8y5i4n/IDZob+VQBvP0v1OFbUBl57NsN3UtTXdTo/Njecl2fwgA==}
peerDependencies:
typescript: '*'
vue-component-type-helpers: 3.0.1
vue-component-type-helpers@2.2.12:
resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
vue-demi@0.14.10: vue-demi@0.14.10:
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@@ -18698,107 +18686,6 @@ snapshots:
- yaml - yaml
optional: true optional: true
astro@5.10.1(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0):
dependencies:
'@astrojs/compiler': 2.12.2
'@astrojs/internal-helpers': 0.6.1
'@astrojs/markdown-remark': 6.3.2
'@astrojs/telemetry': 3.3.0
'@capsizecss/unpack': 2.4.0(encoding@0.1.13)
'@oslojs/encoding': 1.1.0
'@rollup/pluginutils': 5.2.0(rollup@4.45.1)
acorn: 8.15.0
aria-query: 5.3.2
axobject-query: 4.1.0
boxen: 8.0.1
ci-info: 4.2.0
clsx: 2.1.1
common-ancestor-path: 1.0.1
cookie: 1.0.2
cssesc: 3.0.0
debug: 4.4.1
deterministic-object-hash: 2.0.2
devalue: 5.1.1
diff: 5.2.0
dlv: 1.1.3
dset: 3.1.4
es-module-lexer: 1.7.0
esbuild: 0.25.5
estree-walker: 3.0.3
flattie: 1.1.1
fontace: 0.3.0
github-slugger: 2.0.0
html-escaper: 3.0.3
http-cache-semantics: 4.2.0
import-meta-resolve: 4.1.0
js-yaml: 4.1.0
kleur: 4.1.5
magic-string: 0.30.17
magicast: 0.3.5
mrmime: 2.0.1
neotraverse: 0.6.18
p-limit: 6.2.0
p-queue: 8.1.0
package-manager-detector: 1.3.0
picomatch: 4.0.2
prompts: 2.4.2
rehype: 13.0.2
semver: 7.7.2
shiki: 3.8.1
tinyexec: 0.3.2
tinyglobby: 0.2.14
tsconfck: 3.1.6(typescript@5.8.3)
ultrahtml: 1.6.0
unifont: 0.5.2
unist-util-visit: 5.0.0
unstorage: 1.16.0
vfile: 6.0.3
vite: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)
vitefu: 1.0.7(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))
xxhash-wasm: 1.1.0
yargs-parser: 21.1.1
yocto-spinner: 0.2.3
zod: 3.25.76
zod-to-json-schema: 3.24.6(zod@3.25.76)
zod-to-ts: 1.2.0(typescript@5.8.3)(zod@3.25.76)
optionalDependencies:
sharp: 0.33.5
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
- '@azure/data-tables'
- '@azure/identity'
- '@azure/keyvault-secrets'
- '@azure/storage-blob'
- '@capacitor/preferences'
- '@deno/kv'
- '@netlify/blobs'
- '@planetscale/database'
- '@types/node'
- '@upstash/redis'
- '@vercel/blob'
- '@vercel/kv'
- aws4fetch
- db0
- encoding
- idb-keyval
- ioredis
- jiti
- less
- lightningcss
- rollup
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- typescript
- uploadthing
- yaml
optional: true
async-mutex@0.3.2: async-mutex@0.3.2:
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
@@ -25684,7 +25571,7 @@ snapshots:
rollup: 4.45.1 rollup: 4.45.1
vite: rolldown-vite@7.0.9(@types/node@24.0.15)(esbuild@0.25.5)(jiti@2.4.2)(less@4.4.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) vite: rolldown-vite@7.0.9(@types/node@24.0.15)(esbuild@0.25.5)(jiti@2.4.2)(less@4.4.0)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)
unplugin-yaml@3.0.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(astro@5.10.1(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0))(esbuild@0.25.5)(rolldown@1.0.0-beta.29)(rollup@4.45.1)(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)): unplugin-yaml@3.0.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(astro@5.10.1(@types/node@24.0.15)(encoding@0.1.13)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0))(esbuild@0.25.5)(rolldown@1.0.0-beta.29)(rollup@4.45.1)(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)):
dependencies: dependencies:
'@rollup/pluginutils': 5.2.0(rollup@4.45.1) '@rollup/pluginutils': 5.2.0(rollup@4.45.1)
unplugin: 2.3.5 unplugin: 2.3.5
@@ -25692,13 +25579,13 @@ snapshots:
optionalDependencies: optionalDependencies:
'@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.45.1) '@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.45.1)
'@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.45.1) '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.45.1)
astro: 5.10.1(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) astro: 5.10.1(@types/node@24.0.15)(encoding@0.1.13)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0)
esbuild: 0.25.5 esbuild: 0.25.5
rolldown: 1.0.0-beta.29 rolldown: 1.0.0-beta.29
rollup: 4.45.1 rollup: 4.45.1
vite: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) vite: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)
unplugin-yaml@3.0.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(astro@5.10.1(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0))(esbuild@0.25.5)(rolldown@1.0.0-beta.29)(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)): unplugin-yaml@3.0.2(@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@4.45.1))(astro@5.10.1(@types/node@24.0.15)(encoding@0.1.13)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0))(esbuild@0.25.5)(rolldown@1.0.0-beta.29)(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)):
dependencies: dependencies:
'@rollup/pluginutils': 5.2.0(rollup@4.45.1) '@rollup/pluginutils': 5.2.0(rollup@4.45.1)
unplugin: 2.3.5 unplugin: 2.3.5
@@ -25706,7 +25593,7 @@ snapshots:
optionalDependencies: optionalDependencies:
'@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.45.1) '@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.45.1)
'@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.45.1) '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.45.1)
astro: 5.10.1(@types/node@24.0.15)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) astro: 5.10.1(@types/node@24.0.15)(encoding@0.1.13)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(rollup@4.45.1)(terser@5.43.1)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0)
esbuild: 0.25.5 esbuild: 0.25.5
rolldown: 1.0.0-beta.29 rolldown: 1.0.0-beta.29
rollup: 4.45.1 rollup: 4.45.1
@@ -26204,16 +26091,6 @@ snapshots:
vscode-uri@3.0.8: {} vscode-uri@3.0.8: {}
vue-component-meta@3.0.3(typescript@5.8.3)(vue-component-type-helpers@2.2.12):
dependencies:
'@volar/typescript': 2.4.20
'@vue/language-core': 3.0.3(typescript@5.8.3)
path-browserify: 1.0.1
typescript: 5.8.3
vue-component-type-helpers: 2.2.12
vue-component-type-helpers@2.2.12: {}
vue-demi@0.14.10(vue@3.5.17(typescript@5.8.3)): vue-demi@0.14.10(vue@3.5.17(typescript@5.8.3)):
dependencies: dependencies:
vue: 3.5.17(typescript@5.8.3) vue: 3.5.17(typescript@5.8.3)