mirror of
https://github.com/moeru-ai/airi.git
synced 2026-08-14 00:48:06 +00:00
chore: bump dependencies, added script to download live2d sdk
This commit is contained in:
@@ -1,80 +1,17 @@
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/11247099/140462375-7b7ac4db-35b7-453c-8a05-13d8d20282c4.png" width="600"/>
|
||||
</p>
|
||||
|
||||
<h2 align="center">
|
||||
<a href="https://github.com/antfu/vitesse">Vitesse</a> for Nuxt 3
|
||||
</h2><br>
|
||||
<h1 align="center">アイリ VTuber</h1>
|
||||
|
||||
<p align="center">
|
||||
<br>
|
||||
<a href="https://vitesse-nuxt3.netlify.app/">🖥 Online Preview</a>
|
||||
<br><br>
|
||||
<a href="https://stackblitz.com/github/antfu/vitesse-nuxt3"><img src="https://developer.stackblitz.com/img/open_in_stackblitz.svg" alt=""></a>
|
||||
[<a href="https://airi.ayaka.io">Try it</a>]
|
||||
</p>
|
||||
|
||||
## Features
|
||||
> Heavily inspired by [Neuro-sama](https://www.youtube.com/@Neurosama)
|
||||
|
||||
- 💚 [Nuxt 3](https://nuxt.com/) - SSR, ESR, File-based routing, components auto importing, modules, etc.
|
||||
## Development
|
||||
|
||||
- ⚡️ Vite - Instant HMR.
|
||||
```shell
|
||||
pnpm i
|
||||
```
|
||||
|
||||
- 🎨 [UnoCSS](https://github.com/unocss/unocss) - The instant on-demand atomic CSS engine.
|
||||
|
||||
- 😃 Use icons from any icon sets in Pure CSS, powered by [UnoCSS](https://github.com/unocss/unocss).
|
||||
|
||||
- 🔥 The `<script setup>` syntax.
|
||||
|
||||
- 🍍 [State Management via Pinia](https://github.com/vuejs/pinia), see [./composables/user.ts](./composables/user.ts).
|
||||
|
||||
- 📑 [Layout system](./layouts).
|
||||
|
||||
- 📥 APIs auto importing - for Composition API, VueUse and custom composables.
|
||||
|
||||
- 🏎 Zero-config cloud functions and deploy.
|
||||
|
||||
- 🦾 TypeScript, of course.
|
||||
|
||||
- 📲 [PWA](https://github.com/vite-pwa/nuxt) with offline support and auto-update behavior.
|
||||
|
||||
## Plugins
|
||||
|
||||
### Nuxt Modules
|
||||
|
||||
- [VueUse](https://github.com/vueuse/vueuse) - collection of useful composition APIs.
|
||||
- [ColorMode](https://github.com/nuxt-modules/color-mode) - dark and Light mode with auto detection made easy with Nuxt.
|
||||
- [UnoCSS](https://github.com/unocss/unocss) - the instant on-demand atomic CSS engine.
|
||||
- [Pinia](https://github.com/vuejs/pinia) - intuitive, type safe, light and flexible Store for Vue.
|
||||
- [VitePWA](https://github.com/vite-pwa/nuxt) - zero-config PWA Plugin for Nuxt 3.
|
||||
- [DevTools](https://github.com/nuxt/devtools) - unleash Nuxt Developer Experience.
|
||||
|
||||
## IDE
|
||||
|
||||
We recommend using [VS Code](https://code.visualstudio.com/) with [Volar](https://github.com/johnsoncodehk/volar) to get the best experience (You might want to disable [Vetur](https://vuejs.github.io/vetur/) if you have it).
|
||||
|
||||
## Variations
|
||||
|
||||
- [vitesse](https://github.com/antfu/vitesse) - Opinionated Vite Starter Template
|
||||
- [vitesse-lite](https://github.com/antfu/vitesse-lite) - Lightweight version of Vitesse
|
||||
- [vitesse-nuxt-bridge](https://github.com/antfu/vitesse-nuxt-bridge) - Vitesse for Nuxt 2 with Bridge
|
||||
- [vitesse-webext](https://github.com/antfu/vitesse-webext) - WebExtension Vite starter template
|
||||
|
||||
## Try it now!
|
||||
|
||||
### Online
|
||||
|
||||
<a href="https://stackblitz.com/github/antfu/vitesse-nuxt3"><img src="https://developer.stackblitz.com/img/open_in_stackblitz.svg" alt=""></a>
|
||||
|
||||
### GitHub Template
|
||||
|
||||
[Create a repo from this template on GitHub](https://github.com/antfu/vitesse-nuxt3/generate).
|
||||
|
||||
### Clone to local
|
||||
|
||||
If you prefer to do it manually with the cleaner git history
|
||||
|
||||
```bash
|
||||
npx degit antfu/vitesse-nuxt3 my-nuxt3-app
|
||||
cd my-nuxt3-app
|
||||
pnpm i # If you don't have pnpm installed, run: npm install -g pnpm
|
||||
```shell
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useElementBounding } from '@vueuse/core'
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
const containerRef = ref<HTMLDivElement>()
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts" generic="T extends any, O extends any">
|
||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import { Application } from '@pixi/app'
|
||||
import { extensions } from '@pixi/extensions'
|
||||
import { Ticker, TickerPlugin } from '@pixi/ticker'
|
||||
import { Live2DModel, MotionPreloadStrategy, MotionPriority } from 'pixi-live2d-display/cubism4'
|
||||
import { useElementBounding, useWindowSize } from '@vueuse/core'
|
||||
import { Live2DModel, MotionPreloadStrategy, MotionPriority } from 'pixi-live2d-display/cubism4'
|
||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
model: string
|
||||
|
||||
+11
-11
@@ -1,35 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import type {
|
||||
CoreAssistantMessage,
|
||||
CoreSystemMessage,
|
||||
CoreUserMessage,
|
||||
} from 'ai'
|
||||
|
||||
import { llmInferenceEndToken } from '../constants'
|
||||
import type {
|
||||
Emotion,
|
||||
} from '../constants/emotions'
|
||||
import {
|
||||
EMOTION_EmotioMotionName_value,
|
||||
EmotionThinkMotionName,
|
||||
} from '../constants/emotions'
|
||||
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import Avatar from '../assets/live2d/models/hiyori_free_zh/avatar.png'
|
||||
import { useLLM } from '../stores/llm'
|
||||
import { useMarkdown } from '../composables/markdown'
|
||||
|
||||
import { useQueue } from '../composables/queue'
|
||||
import {
|
||||
useDelayMessageQueue,
|
||||
useEmotionsMessageQueue,
|
||||
useMessageContentQueue,
|
||||
} from '../composables/queues'
|
||||
import { useMarkdown } from '../composables/markdown'
|
||||
import { llmInferenceEndToken } from '../constants'
|
||||
import {
|
||||
EMOTION_EmotioMotionName_value,
|
||||
EmotionThinkMotionName,
|
||||
} from '../constants/emotions'
|
||||
import SystemPromptV2 from '../constants/prompts/system-v2'
|
||||
import { useLLM } from '../stores/llm'
|
||||
|
||||
import BasicTextarea from './BasicTextarea.vue'
|
||||
// import AudioWaveform from './AudioWaveform.vue'
|
||||
import Live2DViewer from './Live2DViewer.vue'
|
||||
import BasicTextarea from './BasicTextarea.vue'
|
||||
|
||||
const nowSpeakingAvatarBorderOpacityMin = 30
|
||||
const nowSpeakingAvatarBorderOpacityMax = 100
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { unified } from 'unified'
|
||||
import RemarkRehype from 'remark-rehype'
|
||||
import RemarkParse from 'remark-parse'
|
||||
import RehypeStringify from 'rehype-stringify'
|
||||
import RemarkParse from 'remark-parse'
|
||||
import RemarkRehype from 'remark-rehype'
|
||||
import { unified } from 'unified'
|
||||
|
||||
export function useMarkdown() {
|
||||
const instance = unified()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ref } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export interface HandlerContext<T> {
|
||||
data: T
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Emotion } from '../constants/emotions'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { llmInferenceEndToken } from '../constants'
|
||||
import type { Emotion } from '../constants/emotions'
|
||||
import { EMOTION_VALUES } from '../constants/emotions'
|
||||
import { useQueue } from './queue'
|
||||
|
||||
@@ -100,7 +100,7 @@ export function useEmotionsMessageQueue(emotionsQueue: ReturnType<typeof useQueu
|
||||
export function useDelayMessageQueue(useEmotionsMessageQueue: ReturnType<typeof useQueue<string>>) {
|
||||
function splitDelays(content: string) {
|
||||
// doesn't include the emotion, continue
|
||||
if (!(/<\|DELAY:(\d+)\|>/gi.test(content))) {
|
||||
if (!(/<\|DELAY:\d+\|>/i.test(content))) {
|
||||
return {
|
||||
ok: false,
|
||||
delay: 0,
|
||||
@@ -109,7 +109,7 @@ export function useDelayMessageQueue(useEmotionsMessageQueue: ReturnType<typeof
|
||||
}
|
||||
}
|
||||
|
||||
const delayExecArray = /<\|DELAY:(\d+)\|>/gi.exec(content)
|
||||
const delayExecArray = /<\|DELAY:(\d+)\|>/i.exec(content)
|
||||
|
||||
const delay = delayExecArray?.[1]
|
||||
if (!delay) {
|
||||
@@ -214,7 +214,7 @@ export function useMessageContentQueue(ttsQueue: ReturnType<typeof useQueue<stri
|
||||
return
|
||||
}
|
||||
|
||||
const endMarker = /(\.)|(\?)|(!)/
|
||||
const endMarker = /[.?!]/
|
||||
processed.value += ctx.data
|
||||
|
||||
while (processed.value) {
|
||||
|
||||
+8
-2
@@ -1,20 +1,26 @@
|
||||
version: '0.2'
|
||||
version: "0.2"
|
||||
ignorePaths: []
|
||||
dictionaryDefinitions: []
|
||||
dictionaries: []
|
||||
words:
|
||||
- airi-vtuber
|
||||
- composables
|
||||
- elevenlabs
|
||||
- hiyori
|
||||
- iconify
|
||||
- Myriam
|
||||
- nekomeowww
|
||||
- Neuro
|
||||
- nuxi
|
||||
- nuxt
|
||||
- nuxtjs
|
||||
- ofetch
|
||||
- openai
|
||||
- pinia
|
||||
- pixi
|
||||
- rehype
|
||||
- unocss
|
||||
- vueuse
|
||||
- airi-vtuber
|
||||
- Neuro-sama
|
||||
ignoreWords: []
|
||||
import: []
|
||||
|
||||
@@ -7,6 +7,11 @@ export default nuxt(
|
||||
{
|
||||
unocss: true,
|
||||
formatters: true,
|
||||
yaml: false,
|
||||
markdown: false,
|
||||
ignores: [
|
||||
'public/assets/**/*',
|
||||
],
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
+34
-1
@@ -1,4 +1,11 @@
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { mkdir } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { ofetch } from 'ofetch'
|
||||
|
||||
import { appDescription } from './constants/index'
|
||||
import { exists } from './scripts/fs'
|
||||
import { unzip } from './scripts/unzip'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
@@ -9,8 +16,10 @@ export default defineNuxtConfig({
|
||||
'@nuxt/eslint',
|
||||
],
|
||||
|
||||
ssr: false,
|
||||
|
||||
experimental: {
|
||||
// when using generate, payload js assets included in sw precache manifest
|
||||
// when using generate, payload js assets included in sw pre-cache manifest
|
||||
// but missing on offline, disabling extraction it until fixed
|
||||
payloadExtraction: false,
|
||||
renderJsonPayloads: true,
|
||||
@@ -71,9 +80,33 @@ export default defineNuxtConfig({
|
||||
inlineStyles: false,
|
||||
},
|
||||
|
||||
vite: {
|
||||
plugins: [
|
||||
{
|
||||
name: 'live2d-cubism',
|
||||
async configResolved(config) {
|
||||
if (await exists(join(config.root, 'public/assets/js/CubismSdkForWeb-5-r.1'))) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log('Downloading Cubism SDK...')
|
||||
const stream = await ofetch('https://dist.ayaka.moe/npm/live2d-cubism/CubismSdkForWeb-5-r.1.zip', { responseType: 'arrayBuffer' })
|
||||
|
||||
console.log('Unzipping Cubism SDK...')
|
||||
await mkdir(join(config.root, 'public/assets/js'), { recursive: true })
|
||||
await unzip(Buffer.from(stream), join(config.root, 'public/assets/js'))
|
||||
|
||||
console.log('Cubism SDK downloaded and unzipped.')
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
eslint: {
|
||||
config: {
|
||||
standalone: false,
|
||||
},
|
||||
},
|
||||
|
||||
compatibilityDate: '2024-12-02',
|
||||
})
|
||||
|
||||
+36
-33
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.1.0",
|
||||
"packageManager": "pnpm@9.14.4",
|
||||
"scripts": {
|
||||
"build": "nuxi build",
|
||||
"dev:pwa": "VITE_PLUGIN_PWA=true nuxi dev",
|
||||
@@ -11,43 +11,46 @@
|
||||
"start": "node .output/server/index.mjs",
|
||||
"start:generate": "npx serve .output/public",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"typecheck": "vue-tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^2.17.0",
|
||||
"@iconify-json/carbon": "^1.1.32",
|
||||
"@iconify-json/twemoji": "^1.1.15",
|
||||
"@nuxt/devtools": "^1.3.1",
|
||||
"@nuxt/eslint": "^0.3.12",
|
||||
"@nuxtjs/color-mode": "^3.4.1",
|
||||
"@pinia/nuxt": "^0.5.1",
|
||||
"@unocss/eslint-config": "^0.60.0",
|
||||
"@unocss/nuxt": "^0.60.0",
|
||||
"@vueuse/nuxt": "^10.9.0",
|
||||
"consola": "^3.2.3",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-format": "^0.1.1",
|
||||
"nuxt": "^3.11.2",
|
||||
"pinia": "^2.1.7",
|
||||
"typescript": "^5.4.5",
|
||||
"vue-tsc": "^2.0.17"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^0.0.24",
|
||||
"@pixi/app": "6",
|
||||
"@pixi/extensions": "6",
|
||||
"@pixi/interaction": "6",
|
||||
"@pixi/ticker": "6",
|
||||
"@vueuse/components": "^10.11.0",
|
||||
"ai": "^3.1.30",
|
||||
"elevenlabs": "^0.6.0",
|
||||
"ofetch": "^1.3.4",
|
||||
"openai": "^4.49.0",
|
||||
"@ai-sdk/openai": "^1.0.5",
|
||||
"@pixi/app": "^6.5.10",
|
||||
"@pixi/extensions": "^6.5.10",
|
||||
"@pixi/interaction": "^6.5.10",
|
||||
"@pixi/ticker": "^6.5.10",
|
||||
"@vueuse/components": "^12.0.0",
|
||||
"ai": "^4.0.9",
|
||||
"elevenlabs": "^0.18.1",
|
||||
"ofetch": "^1.4.1",
|
||||
"openai": "^4.73.1",
|
||||
"pixi-live2d-display": "^0.4.0",
|
||||
"rehype-stringify": "^10.0.0",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.0",
|
||||
"unified": "^11.0.4",
|
||||
"remark-rehype": "^11.1.1",
|
||||
"unified": "^11.0.5",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^3.11.2",
|
||||
"@iconify-json/carbon": "^1.2.4",
|
||||
"@iconify-json/twemoji": "^1.2.1",
|
||||
"@nuxt/devtools": "^1.6.1",
|
||||
"@nuxt/eslint": "^0.7.2",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@pinia/nuxt": "^0.8.0",
|
||||
"@types/yauzl": "^2.10.3",
|
||||
"@unocss/eslint-config": "^0.65.0-beta.3",
|
||||
"@unocss/nuxt": "^0.65.0-beta.3",
|
||||
"@vueuse/nuxt": "^12.0.0",
|
||||
"consola": "^3.2.3",
|
||||
"eslint": "^9.16.0",
|
||||
"eslint-plugin-format": "^0.1.3",
|
||||
"nuxt": "^3.14.1592",
|
||||
"pinia": "^2.2.8",
|
||||
"typescript": "^5.7.2",
|
||||
"vue-tsc": "^2.1.10",
|
||||
"yauzl": "^3.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useQueue } from '../composables/queue'
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { llmInferenceEndToken } from '../../../constants'
|
||||
import BasicTextarea from '../../../components/BasicTextarea.vue'
|
||||
import { useQueue } from '../../../composables/queue'
|
||||
import { useDelayMessageQueue } from '../../../composables/queues'
|
||||
import BasicTextarea from '../../../components/BasicTextarea.vue'
|
||||
import { llmInferenceEndToken } from '../../../constants'
|
||||
|
||||
const messageInput = ref<string>('')
|
||||
const emotionMessageContentProcessed = ref<string[]>([])
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
import type { Emotion } from '../../../constants/emotions'
|
||||
import { llmInferenceEndToken } from '../../../constants'
|
||||
|
||||
import { ref } from 'vue'
|
||||
import BasicTextarea from '../../../components/BasicTextarea.vue'
|
||||
import { useQueue } from '../../../composables/queue'
|
||||
import { useEmotionsMessageQueue } from '../../../composables/queues'
|
||||
import BasicTextarea from '../../../components/BasicTextarea.vue'
|
||||
import { llmInferenceEndToken } from '../../../constants'
|
||||
|
||||
const messageInput = ref<string>('')
|
||||
const messagesProcessed = ref<string[]>([])
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { llmInferenceEndToken } from '../../../constants'
|
||||
import BasicTextarea from '../../../components/BasicTextarea.vue'
|
||||
import { useQueue } from '../../../composables/queue'
|
||||
import { useMessageContentQueue } from '../../../composables/queues'
|
||||
import BasicTextarea from '../../../components/BasicTextarea.vue'
|
||||
import { llmInferenceEndToken } from '../../../constants'
|
||||
|
||||
const messageInput = ref<string>('')
|
||||
const ttsProcessed = ref<string[]>([])
|
||||
|
||||
Generated
+4388
-4920
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
||||
export function rejectIfError<E = unknown>(error: E | undefined, reject: (error?: E) => void, handler?: (error?: E) => void) {
|
||||
if (error) {
|
||||
reject(error)
|
||||
!!handler && handler(error)
|
||||
}
|
||||
}
|
||||
|
||||
export function onError<E = unknown>(reject: (error?: E) => void, handler?: (error?: E) => void) {
|
||||
return (error?: E) => rejectIfError(error, reject, handler)
|
||||
}
|
||||
|
||||
export function resolveWhenNoError<R = void, E = unknown>(reject: (error?: E) => void, resolve: (result?: R) => void) {
|
||||
return (err: E) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
else {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function noError<
|
||||
T,
|
||||
U extends unknown[],
|
||||
E = unknown,
|
||||
>(
|
||||
reject: (err?: E) => void,
|
||||
fn: (...args: U) => T,
|
||||
): (err: E | undefined, ...args: U) => T | undefined {
|
||||
return (err, ...args) => {
|
||||
if (err) {
|
||||
rejectIfError(err, reject)
|
||||
return
|
||||
}
|
||||
|
||||
return fn(...args)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { stat } from 'node:fs/promises'
|
||||
|
||||
export async function exists(path: string) {
|
||||
try {
|
||||
await stat(path)
|
||||
return true
|
||||
}
|
||||
catch (error) {
|
||||
if (!(error instanceof Error))
|
||||
throw error
|
||||
if (!('code' in error))
|
||||
throw error
|
||||
if (error.code !== 'ENOENT')
|
||||
throw error
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { Buffer } from 'node:buffer'
|
||||
import { createWriteStream, mkdirSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { fromBuffer } from 'yauzl'
|
||||
import { noError, onError, resolveWhenNoError } from './errors'
|
||||
|
||||
/**
|
||||
* Example:
|
||||
*
|
||||
* await unzip("./tim.zip", "./");
|
||||
*
|
||||
* Will create directories:
|
||||
*
|
||||
* ./tim.zip
|
||||
* ./tim
|
||||
*
|
||||
* Originally by [How to unzip to a folder using yauzl? - Stack Overflow](https://stackoverflow.com/questions/63932027/how-to-unzip-to-a-folder-using-yauzl)
|
||||
*
|
||||
* @param buffer Buffer of the zip file.
|
||||
* @param target Path to the folder where the zip folder will be put.
|
||||
*/
|
||||
export async function unzip(buffer: Buffer, target: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fromBuffer(buffer, { lazyEntries: true }, noError(reject, (zipFile) => {
|
||||
// This is the key. We start by reading the first entry.
|
||||
zipFile.readEntry()
|
||||
|
||||
// Now for every entry, we will write a file or dir
|
||||
// to disk. Then call zipFile.readEntry() again to
|
||||
// trigger the next cycle.
|
||||
zipFile.on('entry', (entry) => {
|
||||
// Directories
|
||||
if (/\/$/.test(entry.fileName)) {
|
||||
// Create the directory then read the next entry.
|
||||
mkdirSync(join(target, entry.fileName))
|
||||
zipFile.readEntry()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Files
|
||||
|
||||
// Write the file to disk.
|
||||
zipFile.openReadStream(entry, noError(reject, (readStream) => {
|
||||
const file = createWriteStream(join(target, entry.fileName))
|
||||
readStream.pipe(file)
|
||||
|
||||
// Handle errors
|
||||
file.on('error', onError(reject, zipFile.close))
|
||||
// Wait until the file is finished writing, then read the next entry.
|
||||
file.on('finish', () => file.close(() => { zipFile.readEntry() }))
|
||||
}))
|
||||
})
|
||||
|
||||
zipFile.on('error', onError(reject, zipFile.close))
|
||||
zipFile.on('end', resolveWhenNoError(reject, resolve))
|
||||
}))
|
||||
})
|
||||
}
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import type { CoreMessage } from 'ai'
|
||||
import { createOpenAI, type OpenAIProvider, type OpenAIProviderSettings } from '@ai-sdk/openai'
|
||||
import { streamText } from 'ai'
|
||||
import { type OpenAIProvider, type OpenAIProviderSettings, createOpenAI } from '@ai-sdk/openai'
|
||||
import { OpenAI } from 'openai'
|
||||
import { ref } from 'vue'
|
||||
import { ofetch } from 'ofetch'
|
||||
import { OpenAI } from 'openai'
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const useLLM = defineStore('llm', () => {
|
||||
const openAI = ref<OpenAI>()
|
||||
|
||||
Reference in New Issue
Block a user