style: lint

This commit is contained in:
Neko Ayaka
2026-08-26 19:49:58 +08:00
parent e60a04a4ec
commit 98f40d7d0b
1625 changed files with 75216 additions and 75203 deletions
+11 -11
View File
@@ -7,25 +7,25 @@ const serverURL = env.CAPACITOR_DEV_SERVER_URL
const appId = argv.includes('android') ? 'ai.moeru.airi_pocket' : 'ai.moeru.airi-pocket'
const config: CapacitorConfig = {
appId,
appName: 'AIRI',
webDir: 'dist',
server: serverURL
? {
url: serverURL,
cleartext: false,
}
: undefined,
android: {
buildOptions: {
keystorePath: env.CAPACITOR_ANDROID_KEYSTORE_PATH,
keystoreAlias: env.CAPACITOR_ANDROID_KEYSTORE_ALIAS,
keystorePassword: env.CAPACITOR_ANDROID_KEYSTORE_PASSWORD,
keystoreAliasPassword: env.CAPACITOR_ANDROID_KEYSTORE_ALIAS_PASSWORD,
keystorePassword: env.CAPACITOR_ANDROID_KEYSTORE_PASSWORD,
keystorePath: env.CAPACITOR_ANDROID_KEYSTORE_PATH,
releaseType: 'APK',
signingType: 'apksigner',
},
},
appId,
appName: 'AIRI',
server: serverURL
? {
cleartext: false,
url: serverURL,
}
: undefined,
webDir: 'dist',
}
export default config
@@ -9,7 +9,7 @@ export function useAudioInput() {
const audioInputs = computed(() => devices.audioInputs.value)
const constraints = ref<MediaStreamConstraints>({ audio: true })
const media = useUserMedia({ constraints, autoSwitch: true, enabled: false })
const media = useUserMedia({ autoSwitch: true, constraints, enabled: false })
async function request() {
if (devices.permissionGranted.value) {
@@ -71,13 +71,13 @@ export function useAudioInput() {
}
return {
selectedAudioInputId,
selectedAudioInput,
audioInputs,
media,
request,
selectedAudioInput,
selectedAudioInputId,
start,
stop,
request,
media,
}
}
@@ -22,8 +22,8 @@ export function useIconAnimation(icon: string) {
})
return {
animationIcon,
iconAnimationStarted,
showIconAnimation,
animationIcon,
}
}
+2 -2
View File
@@ -62,9 +62,9 @@ const routeRecords = setupLayouts(routes as RouteRecordRaw[])
let router: Router
if (isEnvTruthy(import.meta.env.VITE_APP_TARGET_HUGGINGFACE_SPACE))
router = createRouter({ routes: routeRecords, history: createWebHashHistory() })
router = createRouter({ history: createWebHashHistory(), routes: routeRecords })
else
router = createRouter({ routes: routeRecords, history: createWebHistory() })
router = createRouter({ history: createWebHistory(), routes: routeRecords })
router.beforeEach((to, from) => {
if (to.path !== from.path)
+1 -1
View File
@@ -15,8 +15,8 @@ function getLocale() {
}
export const i18n = createI18n({
fallbackLocale: 'en',
legacy: false,
locale: getLocale(),
fallbackLocale: 'en',
messages,
})
@@ -1,12 +1,12 @@
import { registerPlugin } from '@capacitor/core'
interface MicrophonePermissionState {
granted: boolean
}
interface MicrophonePermissionPlugin {
checkPermission: () => Promise<MicrophonePermissionState>
}
interface MicrophonePermissionState {
granted: boolean
}
/** Reads Android's native microphone permission state without triggering a permission request. */
export const MicrophonePermission = registerPlugin<MicrophonePermissionPlugin>('MicrophonePermission')
@@ -21,11 +21,11 @@ export async function probeServerChannelQrPayload(payload: ServerChannelQrPayloa
const client = new Client({
autoConnect: false,
autoReconnect: false,
connector: createTextProtocolConnector(connector),
connectTimeoutMs: 2_000,
name: WebSocketEventSource.StageWeb,
token: payload.authToken,
url,
connector: createTextProtocolConnector(connector),
})
try {
@@ -5,13 +5,13 @@ interface WebAuthenticationOptions {
url: string
}
interface WebAuthenticationResult {
callbackUrl?: string
}
interface WebAuthenticationPlugin {
authenticate: (options: WebAuthenticationOptions) => Promise<WebAuthenticationResult>
}
interface WebAuthenticationResult {
callbackUrl?: string
}
/** Opens an authorization URL with the native system browser session. */
export const WebAuthentication = registerPlugin<WebAuthenticationPlugin>('WebAuthentication')
@@ -1,18 +1,21 @@
import type { ClientConnector, ClientEvents } from '@proj-airi/server-sdk'
type HostBridgeCommand
= | { kind: 'connect', id: string, url: string }
| { kind: 'send', id: string, data: string }
| { kind: 'close', id: string, code?: number, reason?: string }
= | { code?: number, id: string, kind: 'close', reason?: string }
| { data: string, id: string, kind: 'send' }
| { id: string, kind: 'connect', url: string }
type HostBridgeEvent
= | { kind: 'open', id: string }
| { kind: 'message', id: string, data: string }
| { kind: 'error', id: string, message: string }
| { kind: 'close', id: string, code?: number, reason?: string }
= | { code?: number, id: string, kind: 'close', reason?: string }
| { data: string, id: string, kind: 'message' }
| { id: string, kind: 'error', message: string }
| { id: string, kind: 'open' }
declare global {
interface Window {
__airiHostBridge?: {
onNativeMessage?: (payload: string) => void
}
AiriHostBridge?: {
postMessage: (payload: string) => void
}
@@ -23,38 +26,11 @@ declare global {
}
}
}
__airiHostBridge?: {
onNativeMessage?: (payload: string) => void
}
}
}
const connections = new Map<string, HostBridgeConnection>()
function postBridgeMessage(command: HostBridgeCommand) {
if (window.AiriHostBridge) {
window.AiriHostBridge.postMessage(JSON.stringify(command))
return
}
if (window.webkit?.messageHandlers?.airiHostBridge) {
window.webkit.messageHandlers.airiHostBridge.postMessage(JSON.stringify(command))
return
}
throw new Error('AIRI host websocket bridge is unavailable')
}
function dispatchNativeEvent(payload: string) {
const event = JSON.parse(payload) as HostBridgeEvent
const connection = connections.get(event.id)
if (!connection) {
return
}
connection.handleNativeEvent(event)
}
class HostBridgeConnection {
readonly id = crypto.randomUUID()
private opened = false
@@ -69,49 +45,37 @@ class HostBridgeConnection {
connections.set(this.id, this)
postBridgeMessage({
kind: 'connect',
id: this.id,
kind: 'connect',
url: this.url,
})
}
send(data: string) {
if (!this.opened) {
return false
}
postBridgeMessage({
kind: 'send',
id: this.id,
data,
})
return true
}
close(code?: number, reason?: string) {
if (this.settled && !this.opened) {
return
}
postBridgeMessage({
kind: 'close',
id: this.id,
code,
id: this.id,
kind: 'close',
reason,
})
}
handleNativeEvent(event: HostBridgeEvent) {
switch (event.kind) {
case 'open':
this.opened = true
this.settled = true
this.resolve()
break
case 'close':
connections.delete(this.id)
if (!this.settled) {
this.settled = true
this.reject(createCloseBeforeOpenError(event))
return
}
case 'message':
this.events.message(event.data)
this.opened = false
this.events.close({ code: event.code, reason: event.reason })
break
case 'error':
@@ -125,25 +89,31 @@ class HostBridgeConnection {
this.events.error(new Error(event.message))
break
case 'close':
connections.delete(this.id)
if (!this.settled) {
this.settled = true
this.reject(createCloseBeforeOpenError(event))
return
}
case 'message':
this.events.message(event.data)
break
this.opened = false
this.events.close({ code: event.code, reason: event.reason })
case 'open':
this.opened = true
this.settled = true
this.resolve()
break
}
}
}
function createCloseBeforeOpenError(event: Extract<HostBridgeEvent, { kind: 'close' }>) {
const reason = event.reason ? ` ${event.reason}` : ''
const code = typeof event.code === 'number' ? ` with code ${event.code}` : ''
return new Error(`AIRI host websocket bridge closed before opening${code}.${reason}`)
send(data: string) {
if (!this.opened) {
return false
}
postBridgeMessage({
data,
id: this.id,
kind: 'send',
})
return true
}
}
export function getHostWebSocketConnector(url: string): ClientConnector<string> | undefined {
@@ -168,10 +138,40 @@ export function getHostWebSocketConnector(url: string): ClientConnector<string>
}
return {
send: message => activeConnection.send(message),
close: (code?: number, reason?: string) => activeConnection.close(code, reason),
send: message => activeConnection.send(message),
}
})
},
}
}
function createCloseBeforeOpenError(event: Extract<HostBridgeEvent, { kind: 'close' }>) {
const reason = event.reason ? ` ${event.reason}` : ''
const code = typeof event.code === 'number' ? ` with code ${event.code}` : ''
return new Error(`AIRI host websocket bridge closed before opening${code}.${reason}`)
}
function dispatchNativeEvent(payload: string) {
const event = JSON.parse(payload) as HostBridgeEvent
const connection = connections.get(event.id)
if (!connection) {
return
}
connection.handleNativeEvent(event)
}
function postBridgeMessage(command: HostBridgeCommand) {
if (window.AiriHostBridge) {
window.AiriHostBridge.postMessage(JSON.stringify(command))
return
}
if (window.webkit?.messageHandlers?.airiHostBridge) {
window.webkit.messageHandlers.airiHostBridge.postMessage(JSON.stringify(command))
return
}
throw new Error('AIRI host websocket bridge is unavailable')
}
+52 -52
View File
@@ -7,30 +7,30 @@ import { AutoModel, Tensor } from '@huggingface/transformers'
* Voice Activity Detection processor
*/
export class VAD implements BaseVAD {
private config: BaseVADConfig
private model: PreTrainedModel | undefined
private state: Tensor
private sampleRateTensor: Tensor
private buffer: Float32Array
private bufferPointer: number = 0
private config: BaseVADConfig
private eventListeners: Partial<Record<keyof VADEvents, VADEventCallback<any>[]>> = {}
private inferenceChain: Promise<any> = Promise.resolve()
private isReady: boolean = false
private isRecording: boolean = false
private model: PreTrainedModel | undefined
private postSpeechSamples: number = 0
private prevBuffers: Float32Array[] = []
private inferenceChain: Promise<any> = Promise.resolve()
private eventListeners: Partial<Record<keyof VADEvents, VADEventCallback<any>[]>> = {}
private isReady: boolean = false
private sampleRateTensor: Tensor
private state: Tensor
constructor(userConfig: Partial<BaseVADConfig> = {}) {
// Default configuration
const defaultConfig: BaseVADConfig = {
sampleRate: 16000,
speechThreshold: 0.3,
exitThreshold: 0.1,
minSilenceDurationMs: 400,
speechPadMs: 80,
minSpeechDurationMs: 250,
maxBufferDuration: 30,
minSilenceDurationMs: 400,
minSpeechDurationMs: 250,
newBufferSize: 512,
sampleRate: 16000,
speechPadMs: 80,
speechThreshold: 0.3,
}
this.config = { ...defaultConfig, ...userConfig }
@@ -45,7 +45,7 @@ export class VAD implements BaseVAD {
*/
public async initialize(): Promise<void> {
try {
this.emit('status', { type: 'info', message: 'Loading VAD model...' })
this.emit('status', { message: 'Loading VAD model...', type: 'info' })
this.model = await AutoModel.from_pretrained('onnx-community/silero-vad', {
config: { model_type: 'custom' } as any,
@@ -53,24 +53,14 @@ export class VAD implements BaseVAD {
})
this.isReady = true
this.emit('status', { type: 'info', message: 'VAD model loaded successfully' })
this.emit('status', { message: 'VAD model loaded successfully', type: 'info' })
}
catch (error) {
this.emit('status', { type: 'error', message: `Failed to load VAD model: ${error}` })
this.emit('status', { message: `Failed to load VAD model: ${error}`, type: 'error' })
throw error
}
}
/**
* Add event listener
*/
public on<K extends keyof VADEvents>(event: K, callback: VADEventCallback<K>): void {
if (!this.eventListeners[event]) {
this.eventListeners[event] = []
}
this.eventListeners[event]!.push(callback as any)
}
/**
* Remove event listener
*/
@@ -81,14 +71,13 @@ export class VAD implements BaseVAD {
}
/**
* Emit event
* Add event listener
*/
private emit<K extends keyof VADEvents>(event: K, data: VADEvents[K]): void {
if (!this.eventListeners[event])
return
for (const callback of this.eventListeners[event]!) {
callback(data)
public on<K extends keyof VADEvents>(event: K, callback: VADEventCallback<K>): void {
if (!this.eventListeners[event]) {
this.eventListeners[event] = []
}
this.eventListeners[event]!.push(callback as any)
}
/**
@@ -144,7 +133,7 @@ export class VAD implements BaseVAD {
if (!this.isRecording) {
// Speech just started
this.emit('speech-start', undefined)
this.emit('status', { type: 'info', message: 'Speech detected' })
this.emit('status', { message: 'Speech detected', type: 'info' })
}
// Update state
@@ -170,13 +159,31 @@ export class VAD implements BaseVAD {
}
}
/**
* Update configuration
*/
public updateConfig(newConfig: Partial<BaseVADConfig>): void {
this.config = { ...this.config, ...newConfig }
// If buffer size changed, create a new buffer
if (newConfig.maxBufferDuration || newConfig.sampleRate) {
this.buffer = new Float32Array(this.config.maxBufferDuration * this.config.sampleRate)
this.bufferPointer = 0
}
// Update sample rate tensor if needed
if (newConfig.sampleRate) {
this.sampleRateTensor = new Tensor('int64', [this.config.sampleRate], [])
}
}
/**
* Detect speech in an audio buffer
*/
private async detectSpeech(buffer: Float32Array): Promise<boolean> {
const input = new Tensor('float32', buffer, [1, buffer.length])
const { stateN, output } = await (this.inferenceChain = this.inferenceChain.then(() =>
const { output, stateN } = await (this.inferenceChain = this.inferenceChain.then(() =>
this.model?.({
input,
sr: this.sampleRateTensor,
@@ -189,7 +196,7 @@ export class VAD implements BaseVAD {
// Get the speech probability
const speechProb = output.data[0]
this.emit('debug', { message: 'VAD score', data: { probability: speechProb } })
this.emit('debug', { data: { probability: speechProb }, message: 'VAD score' })
// Apply thresholds
return (
@@ -198,6 +205,17 @@ export class VAD implements BaseVAD {
)
}
/**
* Emit event
*/
private emit<K extends keyof VADEvents>(event: K, data: VADEvents[K]): void {
if (!this.eventListeners[event])
return
for (const callback of this.eventListeners[event]!) {
callback(data)
}
}
/**
* Process a complete speech segment
*/
@@ -247,24 +265,6 @@ export class VAD implements BaseVAD {
this.postSpeechSamples = 0
this.prevBuffers = []
}
/**
* Update configuration
*/
public updateConfig(newConfig: Partial<BaseVADConfig>): void {
this.config = { ...this.config, ...newConfig }
// If buffer size changed, create a new buffer
if (newConfig.maxBufferDuration || newConfig.sampleRate) {
this.buffer = new Float32Array(this.config.maxBufferDuration * this.config.sampleRate)
this.bufferPointer = 0
}
// Update sample rate tensor if needed
if (newConfig.sampleRate) {
this.sampleRateTensor = new Tensor('int64', [this.config.sampleRate], [])
}
}
}
/**
+3 -3
View File
@@ -11,15 +11,15 @@ export default mergeConfigs([
...presetWebFontsFonts('fontsource'),
},
timeouts: {
warning: 5000,
failure: 10000,
warning: 5000,
},
}),
],
rules: [
['transition-colors-none', {
'transition-property': 'color, background-color, border-color, text-color',
'transition-duration': '0s',
'transition-property': 'color, background-color, border-color, text-color',
}],
['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
@@ -27,10 +27,10 @@ export default mergeConfigs([
['pl-safe', { 'padding-left': 'env(safe-area-inset-left)' }],
['pr-safe', { 'padding-right': 'env(safe-area-inset-right)' }],
['p-safe', {
'padding-top': 'env(safe-area-inset-top)',
'padding-bottom': 'env(safe-area-inset-bottom)',
'padding-left': 'env(safe-area-inset-left)',
'padding-right': 'env(safe-area-inset-right)',
'padding-top': 'env(safe-area-inset-top)',
}],
],
shortcuts: [
+1 -1
View File
@@ -3,6 +3,6 @@
interface ImportMetaEnv {
readonly VITE_APP_TARGET_HUGGINGFACE_SPACE: string
readonly VITE_PLATFORM: 'ios' | 'android' | 'web'
readonly VITE_PLATFORM: 'android' | 'ios' | 'web'
// more env variables...
}
+1 -1
View File
@@ -1,6 +1,6 @@
declare namespace NodeJS {
export interface ProcessEnv {
VITE_SKIP_MKCERT?: string
VITE_CAP_SYNC_IOS_AFTER_BUILD?: string
VITE_SKIP_MKCERT?: string
}
}
+53 -53
View File
@@ -25,7 +25,7 @@ import { DownloadLive2DSDK } from '@proj-airi/unplugin-live2d-sdk/vite'
import { defineConfig } from 'vite'
// import { isEnvTruthy } from '@proj-airi/stage-shared'
function isEnvTruthy(value: string | undefined | null): boolean {
function isEnvTruthy(value: null | string | undefined): boolean {
if (value == null)
return false
@@ -36,6 +36,9 @@ const stageUIAssetsRoot = resolve(join(import.meta.dirname, '..', '..', 'package
const sharedCacheDir = resolve(join(import.meta.dirname, '..', '..', '.cache'))
export default defineConfig({
build: {
sourcemap: true,
},
optimizeDeps: {
exclude: [
// Internal Packages
@@ -63,46 +66,6 @@ export default defineConfig({
'@framework/model/cubismmoc',
],
},
resolve: {
alias: {
'@proj-airi/server-sdk': resolve(join(import.meta.dirname, '..', '..', 'packages', 'server-sdk', 'src')),
'@proj-airi/i18n': resolve(join(import.meta.dirname, '..', '..', 'packages', 'i18n', 'src')),
'@proj-airi/stage-ui': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-ui', 'src')),
'@proj-airi/stage-layouts': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-layouts', 'src')),
'@proj-airi/stage-pages': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-pages', 'src')),
'@proj-airi/stage-shared': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-shared', 'src')),
},
},
server: {
host: '0.0.0.0',
port: 5273,
fs: {
// To mute errors like:
// The request id ".../node_modules/@fontsource/sniglet/files/sniglet-latin-400-normal.woff" is outside of Vite serving allow list.
//
// See: https://vite.dev/config/server-options#server-fs-strict
strict: false,
},
warmup: {
clientFiles: [
`${resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-ui', 'src'))}/*.vue`,
`${resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-pages', 'src'))}/*.vue`,
`${resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-layouts', 'src'))}/*.vue`,
],
},
},
build: {
sourcemap: true,
},
worker: {
format: 'es',
rollupOptions: {
output: {
inlineDynamicImports: false,
},
},
},
plugins: [
...isEnvTruthy(process.env.VITE_SKIP_MKCERT ?? '')
? []
@@ -119,6 +82,7 @@ export default defineConfig({
Yaml(),
VueMacros({
betterDefine: false,
plugins: {
vue: Vue({
include: [/\.vue$/, /\.md$/],
@@ -126,25 +90,24 @@ export default defineConfig({
}),
vueJsx: false,
},
betterDefine: false,
}),
VueRouter({
extensions: ['.vue', '.md'],
dts: resolve(import.meta.dirname, 'src/typed-router.d.ts'),
exclude: ['**/components/**'],
extensions: ['.vue', '.md'],
importMode: 'async',
routesFolder: [
resolve(import.meta.dirname, 'src', 'pages'),
{
src: resolve(import.meta.dirname, '..', '..', 'packages', 'stage-pages', 'src', 'pages'),
exclude: base => [
...base,
'**/settings/connection/index.vue',
'**/settings/modules/beat-sync.vue',
],
src: resolve(import.meta.dirname, '..', '..', 'packages', 'stage-pages', 'src', 'pages'),
},
],
exclude: ['**/components/**'],
}),
// https://github.com/JohnCampionJr/vite-plugin-vue-layouts
@@ -161,36 +124,35 @@ export default defineConfig({
// https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n
VueI18n({
runtimeOnly: true,
compositionOnly: true,
fullInstall: true,
runtimeOnly: true,
}),
// https://github.com/webfansplz/vite-plugin-vue-devtools
VueDevTools(),
DownloadLive2DSDK(),
Download('https://dist.ayaka.moe/live2d-models/hiyori_free_zh.zip', 'hiyori_free_zh.zip', 'live2d/models', { parentDir: stageUIAssetsRoot, cacheDir: sharedCacheDir }),
Download('https://dist.ayaka.moe/live2d-models/hiyori_pro_zh.zip', 'hiyori_pro_zh.zip', 'live2d/models', { parentDir: stageUIAssetsRoot, cacheDir: sharedCacheDir }),
Download('https://dist.ayaka.moe/vrm-models/VRoid-Hub/AvatarSample-A/AvatarSample_A.vrm', 'AvatarSample_A.vrm', 'vrm/models/AvatarSample-A', { parentDir: stageUIAssetsRoot, cacheDir: sharedCacheDir }),
Download('https://dist.ayaka.moe/vrm-models/VRoid-Hub/AvatarSample-B/AvatarSample_B.vrm', 'AvatarSample_B.vrm', 'vrm/models/AvatarSample-B', { parentDir: stageUIAssetsRoot, cacheDir: sharedCacheDir }),
Download('https://dist.ayaka.moe/live2d-models/hiyori_free_zh.zip', 'hiyori_free_zh.zip', 'live2d/models', { cacheDir: sharedCacheDir, parentDir: stageUIAssetsRoot }),
Download('https://dist.ayaka.moe/live2d-models/hiyori_pro_zh.zip', 'hiyori_pro_zh.zip', 'live2d/models', { cacheDir: sharedCacheDir, parentDir: stageUIAssetsRoot }),
Download('https://dist.ayaka.moe/vrm-models/VRoid-Hub/AvatarSample-A/AvatarSample_A.vrm', 'AvatarSample_A.vrm', 'vrm/models/AvatarSample-A', { cacheDir: sharedCacheDir, parentDir: stageUIAssetsRoot }),
Download('https://dist.ayaka.moe/vrm-models/VRoid-Hub/AvatarSample-B/AvatarSample_B.vrm', 'AvatarSample_B.vrm', 'vrm/models/AvatarSample-B', { cacheDir: sharedCacheDir, parentDir: stageUIAssetsRoot }),
...isEnvTruthy(process.env.VITE_CAP_SYNC_IOS_AFTER_BUILD ?? '')
? [{
name: 'proj-airi:capacitor-sync',
closeBundle: {
sequential: true,
handler() {
if (this.meta.watchMode) {
execSync('cap sync ios', { stdio: 'inherit' })
}
},
sequential: true,
},
name: 'proj-airi:capacitor-sync',
} as PluginOption]
: [],
{
name: 'proj-airi:defines',
config(ctx) {
const define: Record<string, any> = {
'import.meta.env.RUNTIME_ENVIRONMENT': '\'capacitor\'',
@@ -204,6 +166,44 @@ export default defineConfig({
return { define }
},
name: 'proj-airi:defines',
},
],
resolve: {
alias: {
'@proj-airi/i18n': resolve(join(import.meta.dirname, '..', '..', 'packages', 'i18n', 'src')),
'@proj-airi/server-sdk': resolve(join(import.meta.dirname, '..', '..', 'packages', 'server-sdk', 'src')),
'@proj-airi/stage-layouts': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-layouts', 'src')),
'@proj-airi/stage-pages': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-pages', 'src')),
'@proj-airi/stage-shared': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-shared', 'src')),
'@proj-airi/stage-ui': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-ui', 'src')),
},
},
server: {
fs: {
// To mute errors like:
// The request id ".../node_modules/@fontsource/sniglet/files/sniglet-latin-400-normal.woff" is outside of Vite serving allow list.
//
// See: https://vite.dev/config/server-options#server-fs-strict
strict: false,
},
host: '0.0.0.0',
port: 5273,
warmup: {
clientFiles: [
`${resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-ui', 'src'))}/*.vue`,
`${resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-pages', 'src'))}/*.vue`,
`${resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-layouts', 'src'))}/*.vue`,
],
},
},
worker: {
format: 'es',
rollupOptions: {
output: {
inlineDynamicImports: false,
},
},
},
})