style: lint
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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], [])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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: [
|
||||
|
||||
Vendored
+1
-1
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user