import { createApp } from 'vue' import { createPinia } from 'pinia' import './style.css' import App from './App.vue' import router from './router' import i18n from './i18n' import { displayVersion } from '@/utils/version' // Clipboard polyfill for HTTP (non-secure) contexts where navigator.clipboard is unavailable if (!navigator.clipboard) { Object.defineProperty(navigator, 'clipboard', { value: { async writeText(text: string) { const ta = document.createElement('textarea') ta.value = text ta.style.cssText = 'position:fixed;opacity:0' document.body.appendChild(ta) ta.select() document.execCommand('copy') document.body.removeChild(ta) }, async readText() { return '' }, }, }) } import { useToast } from '@/composables/useToast' const app = createApp(App) const pinia = createPinia() app.use(pinia) app.use(router) app.use(i18n) // Global version formatter — normalizes version labels to a single "v" prefix // (some sources already carry one, which produced "vv1.2.3"). Use `$ver(x)` in // templates instead of hard-coding a `v` prefix. app.config.globalProperties.$ver = displayVersion // Keep a small ring buffer of the most recent errors on `window.__archyErrors` // so a crash that only reproduces inside a specific runtime (e.g. the Android // companion WebView, where there's no easy console) can be retrieved after the // fact — read it from chrome://inspect, or we can surface it in a debug view. interface ArchyErrorEntry { when: string; source: string; message: string; info?: string; stack?: string } const errorLog: ArchyErrorEntry[] = [] ;(window as unknown as { __archyErrors?: ArchyErrorEntry[] }).__archyErrors = errorLog function recordError(source: string, err: unknown, info?: string) { const e = err as { message?: string; stack?: string } | undefined const message = (e?.message ?? String(err)) || 'unknown error' const entry: ArchyErrorEntry = { when: new Date().toISOString(), source, message, info, stack: e?.stack } errorLog.push(entry) if (errorLog.length > 25) errorLog.shift() console.error(`[${source}]`, err, info ?? '') // Surface the real message (truncated) instead of a generic toast — this is a // test/bug-bash build, and "Something went wrong" hides exactly what we need. const short = message.length > 140 ? `${message.slice(0, 140)}…` : message try { useToast().error(`Something went wrong: ${short}`) } catch { /* toast itself failed — the console + ring buffer still have it */ } } app.config.errorHandler = (err, _instance, info) => recordError('Vue Error', err, info) // Vue's errorHandler only catches errors raised synchronously inside Vue's // lifecycle/reactivity. Async rejections and plain runtime errors (e.g. a JS // API missing in an older WebView) slip past it, so catch those too. window.addEventListener('error', (ev) => recordError('window.error', ev.error ?? ev.message)) window.addEventListener('unhandledrejection', (ev) => recordError('unhandledrejection', ev.reason)) app.mount('#app')