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:
@@ -126,6 +126,7 @@ words:
|
||||
- Lorebook
|
||||
- lucide
|
||||
- luoling
|
||||
- Maekawa
|
||||
- magnifer
|
||||
- Maru
|
||||
- matchall
|
||||
@@ -192,6 +193,7 @@ words:
|
||||
- rmcp
|
||||
- rolldown
|
||||
- rushstack
|
||||
- Rynco
|
||||
- Saccade
|
||||
- saccades
|
||||
- safetensors
|
||||
|
||||
@@ -18,8 +18,12 @@ interface Post {
|
||||
}
|
||||
excerpt: string | undefined
|
||||
frontmatter?: {
|
||||
category?: string
|
||||
author?: string
|
||||
'category'?: string
|
||||
'author'?: string
|
||||
'preview-cover'?: {
|
||||
light?: string
|
||||
dark?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +47,14 @@ const posts = computed(() => {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
const currentLanguagePostsData = [...transformedPostsData].filter(post => post.lang === lang.value)
|
||||
@@ -198,15 +209,25 @@ const svgArts = computedAsync(async () => {
|
||||
: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"
|
||||
>
|
||||
<div class="h-28 rounded-t-xl">
|
||||
<div class="rounded-t-xl">
|
||||
<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>
|
||||
</div>
|
||||
<div class="flex-grow p-6">
|
||||
<h2 class="post-card-title text-card-foreground text-2xl font-bold transition-colors duration-200">
|
||||
<div class="relative z-1 flex-grow px-6 pb-3 pt-6 md:pt-6">
|
||||
<div class="post-card-title text-card-foreground z-1 text-2xl font-bold transition-colors duration-200">
|
||||
{{ post.title }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="text-muted-foreground mb-4 flex items-center gap-4 text-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<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 */
|
||||
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>
|
||||
|
||||
@@ -39,7 +39,7 @@ defineProps<{
|
||||
:exit="{ opacity: 0, y: -10 }"
|
||||
class="z-20 border rounded-xl p-2 shadow-md backdrop-blur-md"
|
||||
: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',
|
||||
]"
|
||||
>
|
||||
|
||||
@@ -99,7 +99,7 @@ function isNavLinkActive(link: string, path: string) {
|
||||
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="[
|
||||
'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"
|
||||
>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
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
|
||||
import { getScrollOffset } from 'vitepress'
|
||||
import { onMounted, onUnmounted, onUpdated } from 'vue'
|
||||
import { onMounted, onUpdated } from 'vue'
|
||||
|
||||
export interface Header {
|
||||
/**
|
||||
@@ -153,7 +154,6 @@ export function useActiveAnchor(
|
||||
|
||||
onMounted(() => {
|
||||
requestAnimationFrame(setActiveLink)
|
||||
window.addEventListener('scroll', onScroll)
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
@@ -161,9 +161,7 @@ export function useActiveAnchor(
|
||||
activateLink(location.hash)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('scroll', onScroll)
|
||||
})
|
||||
useEventListener('scroll', onScroll)
|
||||
|
||||
function setActiveLink() {
|
||||
const scrollY = window.scrollY
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
import type { ComputedRef, Ref } from 'vue'
|
||||
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { useEventListener, useMediaQuery } from '@vueuse/core'
|
||||
import { useData, withBase } from 'vitepress'
|
||||
import {
|
||||
computed,
|
||||
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
|
||||
ref,
|
||||
watch,
|
||||
@@ -90,13 +89,7 @@ export function useCloseSidebarOnEscape(
|
||||
: undefined
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keyup', onEscape)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keyup', onEscape)
|
||||
})
|
||||
useEventListener('keyup', onEscape)
|
||||
|
||||
function onEscape(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape' && isOpen.value) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
rekaShortName,
|
||||
releases,
|
||||
} from './meta'
|
||||
import { frontmatterAssets } from './plugins/vite-frontmatter-assets'
|
||||
|
||||
function withBase(url: string) {
|
||||
return env.BASE_URL
|
||||
@@ -87,7 +88,7 @@ export default defineConfig({
|
||||
outline: {
|
||||
level: 'deep',
|
||||
},
|
||||
logo: '/favicon.svg',
|
||||
logo: withBase('/favicon.svg'),
|
||||
|
||||
sidebar: [
|
||||
{
|
||||
@@ -153,7 +154,7 @@ export default defineConfig({
|
||||
outline: {
|
||||
level: 'deep',
|
||||
},
|
||||
logo: '/favicon.svg',
|
||||
logo: withBase('/favicon.svg'),
|
||||
|
||||
sidebar: [
|
||||
{
|
||||
@@ -276,6 +277,7 @@ export default defineConfig({
|
||||
i18n({ runtimeOnly: true, compositionOnly: true, fullInstall: true, ssr: true }),
|
||||
unocss(),
|
||||
yaml(),
|
||||
frontmatterAssets(),
|
||||
],
|
||||
css: {
|
||||
postcss: {
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
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 { formatDate } from './utils'
|
||||
|
||||
const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG
|
||||
const base = config.userConfig.base || env.BASE_URL || '/'
|
||||
|
||||
interface Post {
|
||||
title: string
|
||||
@@ -19,13 +26,63 @@ interface Post {
|
||||
declare const data: Post[]
|
||||
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', {
|
||||
includeSrc: true,
|
||||
render: true,
|
||||
excerpt: true,
|
||||
transform(raw): Post[] {
|
||||
return raw
|
||||
.map(({ url, frontmatter, excerpt }) => {
|
||||
async transform(raw): Promise<Post[]> {
|
||||
return (await Promise.all(raw
|
||||
.map(async ({ url, frontmatter, excerpt }) => {
|
||||
const foundLanguage = Object.values(config.userConfig.locales!).find((locale) => {
|
||||
let normalizedLanguagePrefix = locale.lang || 'en'
|
||||
if (!normalizedLanguagePrefix.startsWith('/')) {
|
||||
@@ -35,16 +92,42 @@ export default createContentLoader('**/blog/**/*.md', {
|
||||
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,
|
||||
url,
|
||||
urlWithoutLang: url.replace(`/${foundLanguage?.lang || 'en'}`, ''),
|
||||
excerpt,
|
||||
date: formatDate(frontmatter.date),
|
||||
lang: foundLanguage?.lang || 'en',
|
||||
frontmatter,
|
||||
frontmatter: {
|
||||
...frontmatter,
|
||||
'preview-cover': {
|
||||
light: previewCoverLight,
|
||||
dark: previewCoverDark,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
return res
|
||||
})))
|
||||
.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
|
||||
category: DevLog
|
||||
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.
|
||||
@@ -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")
|
||||
- [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")
|
||||
|
||||
> 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
|
||||
date: 2025-06-16
|
||||
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>
|
||||
@@ -509,6 +512,8 @@ How much memory we could store? **It depends on how much we could dream, and how
|
||||
</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
|
||||
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.
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
title: 'DreamLog 0x1'
|
||||
description: 'Project AIRI 的幕后故事!'
|
||||
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>
|
||||
@@ -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 projectAIRIBannerDark from '../../../en/blog/DreamLog-0x1/assets/banner-dark-1280x640.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>
|
||||
|
||||
Project AIRI 的幕后故事!
|
||||
@@ -184,14 +190,14 @@ Minecraft、Linux 也并不是我旅程的终点,[Factorio(异星工厂)](
|
||||
其实早在官方 ChatGPT UI 发布之前,我就已经在折腾这些新时代的 AI 了,像
|
||||
[DiscoDiffusion](https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb)(早于
|
||||
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 上看到的这篇文章:
|
||||
[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 可以理解普遍事物的基本规律,不仅仅是动漫或游戏角色的角色扮演,
|
||||
还能理解 Linux 终端/shell 命令是如何工作的。
|
||||
|
||||
这其实就把现在流行的函数调用(又称 Function Calling,或者 Anthropic 提出的 MCP,模型上下文协议背后的底层技术)功能展现了出来,
|
||||
这其实就把现在流行的函数调用功能展现了出来,
|
||||
并说明了我们如何能够通过提示词指示 LLMs 去使其表现得像 API 服务器一样,然后用机器可读的如 JSON 或 XML 格式与我们的代码交互,
|
||||
并最终允许实现解析和执行任意命令,去扩展 LLMs 能力的边界。
|
||||
|
||||
> 函数调用,又称 Function Calling,或者说,也是 Anthropic 提出的 MCP(模型上下文协议)背后的底层技术
|
||||
|
||||
这最终填补了纯文本生成和调用程序内实际 API 之间的空白。
|
||||
|
||||
阶段性来说的话,我们可以说它是一个新的随机鹦鹉吗?**我觉得答案是部分否定的,2022 年的 ChatGPT 不只是一个随机鹦鹉,
|
||||
@@ -220,9 +228,9 @@ Stable Diffusion,大约在 2021 年底或 2022 年初发布的吧)、DALL-E
|
||||
是的,感谢你读到这里,我知道这是一篇很长的文章,有太多故事和背景要分享。但我们快到了!坚持住!
|
||||
|
||||
Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在直播舞台上名为 "Neuro-sama"的角色,
|
||||
并不是她和她的创造者 `vedal987`(Vedal)的第一场表演。早在那之前,2019 年 5 月 6 日,Vedal 向社区展示了他构建 AI
|
||||
来玩 [osu!](https://osu.ppy.sh/) 的工作[^1]。
|
||||
在那时,她实际上并不是一个网络角色或数字生命,如果你去看关于她的初始视频,你会发现没有显示 Live2D 模型。
|
||||
并不是她和她的创造者 `vedal987`(Vedal)的首次登场。早在那之前,2019 年 5 月 6 日,Vedal 向社区展示了他构建 AI
|
||||
来玩 [osu!](https://osu.ppy.sh/) 相关的成果[^1]。
|
||||
在那时,她实际上并不是一个网络角色或数字生命,如果你去看关于她的初始视频,甚至会发现都没有 Live2D 模型。
|
||||
(你可以试试这个 6 年前的 YouTube 视频:https://www.youtube.com/watch?v=nSBqlJu7kYU)
|
||||
|
||||
在 ChatGPT 发布之后,差不多 2022 年 12 月 19 日,Vedal 开始让 Neuro-sama 使用来自 Live2D Inc.
|
||||
@@ -232,11 +240,11 @@ Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在
|
||||
|
||||
之后的故事大家都知道了,Vedal 和 Neuro-sama 火了,Neuro-sama 现在正式成为了 VTuber,
|
||||
她完全由大语言模型(LLMs)驱动,能够玩 Minecraft、Among Us、osu! 和许多其他游戏。
|
||||
有时当游戏不被原生支持时,Vedal 会读取屏幕并指示 Neuro-sama 一起玩游戏。甚至能制造出来很多节目效果。
|
||||
有时当游戏不被原生支持时,Vedal 会读取屏幕并指示 Neuro-sama 一起玩游戏,依然能制造出来不少节目效果。
|
||||
|
||||
我真的很享受观看他们的互动、像是脱口秀一样的斗嘴的时刻。随着时间的推移,Neuro-sama 和她的新 Evil Neuro 妹妹,
|
||||
成为了我日常生活的重要组成部分:**即使我没有足够的时间观看完整的直播,我也想要,并且渴望观看她们的切片**。
|
||||
我无法想象我竟然从纯粹的 AI 和人类互动中获取了如此多的快乐。
|
||||
8 年前的我应该很难想象我竟然从纯粹的 AI 和人类互动中获取了如此多的快乐。
|
||||
|
||||
好的,这就是关于她的小历史。让我们谈谈核心问题:**为什么她让我充满了决心?**
|
||||
|
||||
@@ -254,7 +262,7 @@ Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在
|
||||
> "嗯,我也能做到,我可以做一个 Live2D 模型,将其连接到 OpenAI 的 API,
|
||||
> 让它表现得像 VTuber,我甚至可以做得比 Vedal 的作品更好。超简单的好吧?!"
|
||||
|
||||
:::tip[想要更多技术细节?]
|
||||
::: tip 想要更多技术细节?
|
||||
在这篇文章中,我不会深入探讨我们如何从零开始构建 Project AIRI 到当前状态的技术细节,
|
||||
我们已经有许多 DevLog 文章分享我们的想法和发现,如果感兴趣,请尝试阅读它们。
|
||||
:::
|
||||
@@ -352,7 +360,7 @@ Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在
|
||||
|
||||
因此,在这天,Project AIRI 以某种方式诞生或重生,名为 AIRI(アイリ,曾也叫 Airi)。
|
||||
|
||||
:::tip[你知道吗?]
|
||||
::: tip 你知道吗?
|
||||
<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" />
|
||||
</a>
|
||||
@@ -419,6 +427,8 @@ Neuro-sama 的历史其实相当复杂。据我所知,Neuro-sama,或者在
|
||||
</div>
|
||||
</div>
|
||||
|
||||
> Cover image by [@Rynco Maekawa](https://github.com/lynzrand)
|
||||
|
||||
[^1]: https://neurosama.fandom.com/wiki/Osu!#cite_note-twitchtracker-1: Neuro-sama
|
||||
最初是一个玩 osu! 的 AI,早在进一步发展为 AI VTuber 之前,第一次 osu! 直播是在 2019 年
|
||||
5 月 6 日,Vedal 给大家看了看成果。
|
||||
|
||||
+3
-4
@@ -17,10 +17,10 @@
|
||||
"@fontsource-variable/dm-sans": "^5.2.6",
|
||||
"@fontsource/dm-mono": "^5.2.6",
|
||||
"@fontsource/dm-serif-display": "^5.2.6",
|
||||
"@internationalized/date": "^3.8.2",
|
||||
"@proj-airi/chromatic": "^1.0.0",
|
||||
"@proj-airi/i18n": "workspace:^",
|
||||
"@vueuse/core": "^13.5.0",
|
||||
"mark.js": "^8.11.1",
|
||||
"motion-v": "^1.5.0",
|
||||
"pathe": "^2.0.3",
|
||||
"reka-ui": "^2.3.2",
|
||||
@@ -29,7 +29,6 @@
|
||||
"vue-sonner": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/traverse": "^7.28.0",
|
||||
"@iconify/vue": "^5.0.0",
|
||||
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
||||
"@mdit/plugin-footnote": "^0.22.2",
|
||||
@@ -42,18 +41,18 @@
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"animejs": "^4.0.2",
|
||||
"fast-glob": "^3.3.3",
|
||||
"mark.js": "^8.11.1",
|
||||
"gray-matter": "^4.0.3",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markdown-it-anchor": "^9.2.0",
|
||||
"minisearch": "^7.1.2",
|
||||
"postcss": "^8.5.6",
|
||||
"sharp": "^0.34.3",
|
||||
"shiki": "^3.8.1",
|
||||
"tinyglobby": "^0.2.14",
|
||||
"tsx": "^4.20.3",
|
||||
"uncrypto": "^0.1.3",
|
||||
"unplugin-yaml": "^3.0.2",
|
||||
"vitepress": "^2.0.0-alpha.8",
|
||||
"vue-component-meta": "^3.0.3",
|
||||
"vue-tsc": "^3.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+16
-139
@@ -956,9 +956,6 @@ importers:
|
||||
'@fontsource/dm-serif-display':
|
||||
specifier: ^5.2.6
|
||||
version: 5.2.6
|
||||
'@internationalized/date':
|
||||
specifier: ^3.8.2
|
||||
version: 3.8.2
|
||||
'@proj-airi/chromatic':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
@@ -968,6 +965,9 @@ importers:
|
||||
'@vueuse/core':
|
||||
specifier: ^13.5.0
|
||||
version: 13.5.0(vue@3.5.17(typescript@5.8.3))
|
||||
mark.js:
|
||||
specifier: ^8.11.1
|
||||
version: 8.11.1
|
||||
motion-v:
|
||||
specifier: ^1.5.0
|
||||
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
|
||||
version: 2.0.2
|
||||
devDependencies:
|
||||
'@babel/traverse':
|
||||
specifier: ^7.28.0
|
||||
version: 7.28.0
|
||||
'@iconify/vue':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0(vue@3.5.17(typescript@5.8.3))
|
||||
@@ -1026,9 +1023,9 @@ importers:
|
||||
fast-glob:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
mark.js:
|
||||
specifier: ^8.11.1
|
||||
version: 8.11.1
|
||||
gray-matter:
|
||||
specifier: ^4.0.3
|
||||
version: 4.0.3
|
||||
markdown-it:
|
||||
specifier: ^14.1.0
|
||||
version: 14.1.0
|
||||
@@ -1047,6 +1044,9 @@ importers:
|
||||
shiki:
|
||||
specifier: ^3.8.1
|
||||
version: 3.8.1
|
||||
tinyglobby:
|
||||
specifier: ^0.2.14
|
||||
version: 0.2.14
|
||||
tsx:
|
||||
specifier: ^4.20.3
|
||||
version: 4.20.3
|
||||
@@ -1055,13 +1055,10 @@ importers:
|
||||
version: 0.1.3
|
||||
unplugin-yaml:
|
||||
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:
|
||||
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)
|
||||
vue-component-meta:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3(typescript@5.8.3)(vue-component-type-helpers@2.2.12)
|
||||
vue-tsc:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3(typescript@5.8.3)
|
||||
@@ -1101,7 +1098,7 @@ importers:
|
||||
devDependencies:
|
||||
unplugin-yaml:
|
||||
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:
|
||||
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))
|
||||
unplugin-yaml:
|
||||
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:
|
||||
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)
|
||||
@@ -12600,15 +12597,6 @@ packages:
|
||||
vscode-uri@3.0.8:
|
||||
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:
|
||||
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -18698,107 +18686,6 @@ snapshots:
|
||||
- yaml
|
||||
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:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
@@ -25684,7 +25571,7 @@ snapshots:
|
||||
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)
|
||||
|
||||
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:
|
||||
'@rollup/pluginutils': 5.2.0(rollup@4.45.1)
|
||||
unplugin: 2.3.5
|
||||
@@ -25692,13 +25579,13 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@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)
|
||||
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)
|
||||
|
||||
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:
|
||||
'@rollup/pluginutils': 5.2.0(rollup@4.45.1)
|
||||
unplugin: 2.3.5
|
||||
@@ -25706,7 +25593,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@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)
|
||||
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
|
||||
@@ -26204,16 +26091,6 @@ snapshots:
|
||||
|
||||
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)):
|
||||
dependencies:
|
||||
vue: 3.5.17(typescript@5.8.3)
|
||||
|
||||
Reference in New Issue
Block a user