feat(stage-ui): new devtools about background & theme color blending

This commit is contained in:
Neko Ayaka
2025-07-23 01:38:03 +08:00
parent 9dd1601555
commit 5194868d48
6 changed files with 371 additions and 108 deletions
+1
View File
@@ -58,6 +58,7 @@
"drizzle-kit": "^0.31.4",
"drizzle-orm": "^0.44.3",
"gpuu": "^1.0.4",
"html2canvas": "^1.4.1",
"jszip": "^3.10.1",
"localforage": "^1.10.0",
"nanoid": "^5.1.5",
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

@@ -0,0 +1,280 @@
<script setup lang="ts">
import type { Color } from 'culori'
import html2canvas from 'html2canvas'
import { BasicInputFile } from '@proj-airi/ui'
import { average } from 'culori'
import { Vibrant } from 'node-vibrant/browser'
import { computed, nextTick, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue'
import defaultBackgroundImage from '../../assets/backgrounds/fairy-forest.e17cbc2774.ko-fi.com.png'
import { useThemeColor } from '../../composables/theme-color'
// Reactive state
const isCapturing = ref(false)
const extractedColors = ref<string[]>([])
const dominantColor = ref('')
const topEdgeColors = ref('')
const imageCleanups = ref<Array<() => void>>([])
const imageFiles = ref<File[]>([])
const images = computed(() => {
if (imageFiles.value.length === 0)
return [defaultBackgroundImage]
return imageFiles.value.map((file) => {
const url = URL.createObjectURL(file)
imageCleanups.value.push(() => URL.revokeObjectURL(url))
return url
})
})
const mode = ref<'vibrant' | 'html2canvas'>('vibrant')
// Template refs
const imageRef = useTemplateRef<HTMLDivElement>('imageRef')
const canvasRef = useTemplateRef<HTMLCanvasElement>('canvas')
// Theme color integration
const { updateThemeColor } = useThemeColor(() => dominantColor.value)
// Computed gradient for top bar blending
const topBar = computed(() => {
if (mode.value === 'vibrant') {
return dominantColor.value
}
else if (mode.value === 'html2canvas') {
return topEdgeColors.value
}
return ''
})
// Color extraction from image
async function extractColorsFromImage() {
if (images.value.length === 0) {
return
}
try {
isCapturing.value = true
// Extract colors using Vibrant
const vibrant = new Vibrant(images.value[0])
const palette = await vibrant.getPalette()
const colors = Object.values(palette)
.map(color => color?.hex)
.filter((color): color is string => typeof color === 'string')
extractedColors.value = colors
dominantColor.value = palette.Vibrant?.hex || palette.DarkVibrant?.hex || colors[0]
// Update PWA theme color
await updateThemeColor()
}
catch (error) {
console.error('Color extraction failed:', error)
}
finally {
isCapturing.value = false
}
}
// Capture top edge colors using html2canvas
async function captureTopEdgeColors() {
if (!imageRef.value)
return
try {
isCapturing.value = true
// Capture the background element
const canvas = await html2canvas(imageRef.value, {
allowTaint: true,
useCORS: true,
backgroundColor: null,
scale: 0.5, // Reduce quality for performance
height: 100, // Only capture top portion
width: imageRef.value.offsetWidth,
logging: false,
})
// Display captured canvas for debugging
if (canvasRef.value) {
const ctx = canvasRef.value.getContext('2d')
if (ctx) {
canvasRef.value.width = canvas.width
canvasRef.value.height = canvas.height
ctx.drawImage(canvas, 0, 0)
}
}
// Sample colors from top edge
const ctx = canvas.getContext('2d')
if (ctx) {
const imageData = ctx.getImageData(0, 0, canvas.width, 20) // Top 20px
const colors: Color[] = []
// Sample every 10th pixel to get representative colors
for (let i = 0; i < imageData.data.length; i += 40) { // RGBA = 4 bytes, sample every 10 pixels
const r = imageData.data[i]
const g = imageData.data[i + 1]
const b = imageData.data[i + 2]
const a = imageData.data[i + 3]
if (a > 0) { // Skip transparent pixels
colors.push({ mode: 'rgb', r, g, b })
}
}
if (colors.length > 0) {
const c = average(colors as [Color, ...Color[]])
topEdgeColors.value = `rgb(${c.r}, ${c.g}, ${c.b})`
}
}
}
catch (error) {
console.error('Canvas capture failed:', error)
}
finally {
isCapturing.value = false
}
}
// Auto-extract colors on mount
onMounted(async () => {
await nextTick()
await extractColorsFromImage()
await captureTopEdgeColors()
})
watch(images, async () => {
await nextTick()
await extractColorsFromImage()
await captureTopEdgeColors()
})
onUnmounted(() => {
imageCleanups.value.forEach(cleanup => cleanup())
})
</script>
<template>
<div class="h-fit w-full flex flex-col gap-4">
<div class="relative w-full overflow-hidden rounded-xl">
<div class="pointer-events-none left-0 right-0 top-0 z-10 flex items-center justify-center backdrop-blur-md" :style="{ background: topBar }">
<div class="py-4 text-center text-sm text-white font-medium">
Top Area
</div>
</div>
<div class="transparent-gradient-overlay absolute inset-0 h-[calc((1lh+1rem+1rem)*2)] w-full" :style="{ background: topBar }" />
<img
ref="imageRef"
:src="images[0]"
class="h-full max-h-[calc(100dvh-25rem)] w-full object-cover"
>
<!-- Live2D area placeholder -->
<div class="absolute inset-0 flex items-center justify-center">
<BasicInputFile v-model="imageFiles">
<div class="rounded-xl bg-black bg-opacity-30 px-5 py-4 text-white backdrop-blur-sm">
Replace Image
</div>
</BasicInputFile>
</div>
</div>
<!-- Debug information -->
<div class="debug-info flex flex-col" bg="neutral-100 dark:neutral-900" rounded-xl>
<div flex>
<!-- Extracted colors -->
<div class="rounded-lg p-4" flex-1>
<h3 class="mb-1 text-lg" flex items-center gap-2>
<span>Node Vibrant</span>
<button
border-2 border-neutral-300 rounded-xl border-solid px-3 py-1 text-sm font-normal dark:border-neutral-700
:class="[
mode === 'vibrant' ? 'bg-neutral-100 dark:bg-neutral-700' : '',
]"
@click="mode = 'vibrant'"
>
{{ mode === 'vibrant' ? 'Activated' : 'Active' }}
</button>
</h3>
<div class="color-palette flex flex-wrap gap-2">
<div
v-for="color in extractedColors"
:key="color"
class="h-12 w-12 cursor-pointer border-2 border-gray-300 rounded rounded-xl transition-transform duration-200 ease-in-out hover:scale-110 dark:border-gray-900"
:style="{ backgroundColor: color }"
:title="color"
/>
</div>
<p class="mt-2 text-sm">
Color: <span :style="{ color: dominantColor }">{{ dominantColor }}</span>
</p>
</div>
<div class="rounded-lg p-4" flex-1>
<h3 class="mb-1 text-lg" flex items-center gap-2>
<span>html2canvas Top sampling</span>
<button
border-2 border-neutral-300 rounded-xl border-solid px-3 py-1 text-sm font-normal dark:border-neutral-700
:class="[
mode === 'html2canvas' ? 'bg-neutral-100 dark:bg-neutral-700' : '',
]"
@click="mode = 'html2canvas'"
>
{{ mode === 'html2canvas' ? 'Activated' : 'Active' }}
</button>
</h3>
<div class="color-palette flex flex-wrap gap-2">
<div
class="color-swatch h-12 w-12 border-2 border-gray-300 rounded rounded-xl dark:border-gray-900"
:style="{ backgroundColor: topEdgeColors }"
:title="topEdgeColors"
/>
</div>
<p class="mt-2 text-sm">
Color: <span :style="{ color: topEdgeColors }">{{ topEdgeColors }}</span>
</p>
</div>
</div>
<div class="rounded-lg p-4" w-full>
<h3 class="mb-1 text-lg">
Captured Canvas (Debug)
</h3>
<canvas
ref="canvas"
class="max-w-full"
style="max-height: 100px;"
/>
</div>
</div>
</div>
</template>
<style scoped>
/*
DO NOT ATTEMPT TO USE backdrop-filter TOGETHER WITH mask-image.
html - Why doesn't blur backdrop-filter work together with mask-image? - Stack Overflow
https://stackoverflow.com/questions/72780266/why-doesnt-blur-backdrop-filter-work-together-with-mask-image
*/
.transparent-gradient-overlay {
--gradient: linear-gradient(to top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 50%);
-webkit-mask-image: var(--gradient);
mask-image: var(--gradient);
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: bottom;
mask-position: bottom;
}
</style>
@@ -0,0 +1,43 @@
<script setup lang="ts">
import { useDebounce } from '@vueuse/core'
import { ref } from 'vue'
defineProps<{
accept?: string
multiple?: boolean
}>()
const files = defineModel<File[]>({ required: false, default: () => [] })
const firstFile = ref<File>()
const isDragging = ref(false)
const isDraggingDebounced = useDebounce(isDragging, 150)
function handleFileChange(e: Event) {
const input = e.target as HTMLInputElement
if (input.files && input.files.length > 0) {
firstFile.value = input.files[0]
}
files.value = Array.from(input.files || [])
isDragging.value = false
}
</script>
<template>
<label
relative cursor-pointer
@dragover="isDragging = true"
@dragleave="isDragging = false"
>
<input
type="file"
:accept="accept"
:multiple="multiple"
class="absolute inset-0 h-full w-full opacity-0"
@change="handleFileChange"
>
<slot :is-dragging="isDraggingDebounced" :first-file="firstFile" :files="files" />
</label>
</template>
@@ -1,3 +1,4 @@
export { default as BasicInputFile } from './BasicInputFile.vue'
export { default as Input } from './Input.vue'
export { default as InputFile } from './InputFile.vue'
export { default as InputKeyValue } from './InputKeyValue.vue'
+46 -108
View File
@@ -742,6 +742,9 @@ importers:
gpuu:
specifier: ^1.0.4
version: 1.0.4
html2canvas:
specifier: ^1.4.1
version: 1.4.1
jszip:
specifier: ^3.10.1
version: 3.10.1
@@ -1043,7 +1046,7 @@ 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)
@@ -1089,7 +1092,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:
@@ -1463,7 +1466,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)
@@ -6674,6 +6677,10 @@ packages:
base-64@1.0.0:
resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==}
base64-arraybuffer@1.0.2:
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
engines: {node: '>= 0.6.0'}
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@@ -7183,6 +7190,9 @@ packages:
resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
engines: {node: '>=8'}
css-line-break@2.1.0:
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
css-select@5.2.2:
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
@@ -8640,6 +8650,10 @@ packages:
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
html2canvas@1.4.1:
resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
engines: {node: '>=8.0.0'}
htmlparser2@10.0.0:
resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==}
@@ -11561,6 +11575,9 @@ packages:
resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==}
engines: {node: '>=18'}
text-segmentation@1.0.3:
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
thenify-all@1.6.0:
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
engines: {node: '>=0.8'}
@@ -12279,6 +12296,9 @@ packages:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
utrie@1.0.2:
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
uuid-1345@1.0.2:
resolution: {integrity: sha512-bA5zYZui+3nwAc0s3VdGQGBfbVsJLVX7Np7ch2aqcEWFi5lsAEcmO3+lx3djM1npgpZI8KY2FITZ2uYTnYUYyw==}
@@ -18666,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
@@ -18833,6 +18752,8 @@ snapshots:
base-64@1.0.0:
optional: true
base64-arraybuffer@1.0.2: {}
base64-js@1.5.1: {}
base64id@2.0.0: {}
@@ -19382,6 +19303,10 @@ snapshots:
crypto-random-string@2.0.0: {}
css-line-break@2.1.0:
dependencies:
utrie: 1.0.2
css-select@5.2.2:
dependencies:
boolbase: 1.0.0
@@ -21193,6 +21118,11 @@ snapshots:
html-void-elements@3.0.0: {}
html2canvas@1.4.1:
dependencies:
css-line-break: 2.1.0
text-segmentation: 1.0.3
htmlparser2@10.0.0:
dependencies:
domelementtype: 2.3.0
@@ -24828,6 +24758,10 @@ snapshots:
glob: 10.4.5
minimatch: 9.0.5
text-segmentation@1.0.3:
dependencies:
utrie: 1.0.2
thenify-all@1.6.0:
dependencies:
thenify: 3.3.1
@@ -25637,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
@@ -25645,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
@@ -25659,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
@@ -25768,6 +25702,10 @@ snapshots:
utils-merge@1.0.1: {}
utrie@1.0.2:
dependencies:
base64-arraybuffer: 1.0.2
uuid-1345@1.0.2:
dependencies:
macaddress: 0.5.3