mirror of
https://github.com/vrcx-team/VRCX.git
synced 2026-04-06 00:32:02 +02:00
add Sentry error reporting option
This commit is contained in:
121
package-lock.json
generated
121
package-lock.json
generated
@@ -15,10 +15,11 @@
|
||||
"@eslint/js": "^9.35.0",
|
||||
"@fontsource/noto-sans-jp": "^5.2.7",
|
||||
"@fontsource/noto-sans-kr": "^5.2.7",
|
||||
"@fontsource/noto-sans-sc": "^5.2.6",
|
||||
"@fontsource/noto-sans-sc": "^5.2.7",
|
||||
"@fontsource/noto-sans-tc": "^5.2.7",
|
||||
"@sentry/vue": "^10.11.0",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^24.3.1",
|
||||
"@types/node": "^24.3.3",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"animate.css": "^4.1.1",
|
||||
"babel-runtime": "^6.26.0",
|
||||
@@ -26,21 +27,21 @@
|
||||
"cross-env": "^10.0.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"echarts": "^6.0.0",
|
||||
"electron": "^37.4.0",
|
||||
"electron": "^37.5.0",
|
||||
"electron-builder": "^26.0.12",
|
||||
"element-plus": "^2.11.2",
|
||||
"esbuild-jest": "^0.4.0",
|
||||
"eslint": "^9.35.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-vue": "^9.33.0",
|
||||
"globals": "^16.3.0",
|
||||
"globals": "^16.4.0",
|
||||
"jest": "^30.1.3",
|
||||
"noty": "^3.2.0-beta-deprecated",
|
||||
"pinia": "^3.0.3",
|
||||
"prettier": "^3.6.2",
|
||||
"remixicon": "^4.6.0",
|
||||
"sass-embedded": "^1.92.1",
|
||||
"vite": "^7.1.4",
|
||||
"vite": "^7.1.5",
|
||||
"vue": "^3.5.21",
|
||||
"vue-i18n": "^11.1.12",
|
||||
"vue-marquee-text-component": "^2.0.1",
|
||||
@@ -3903,6 +3904,110 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@sentry-internal/browser-utils": {
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.11.0.tgz",
|
||||
"integrity": "sha512-fnMlz5ntap6x4vRsLOHwPqXh7t82StgAiRt+EaqcMX0t9l8C0w0df8qwrONKXvE5GdHWTNFJj5qR15FERSkg3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "10.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/feedback": {
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.11.0.tgz",
|
||||
"integrity": "sha512-ADey51IIaa29kepb8B7aSgSGSrcyT7QZdRsN1rhitefzrruHzpSUci5c2EPIvmWfKJq8Wnvukm9BHXZXAAIOzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "10.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/replay": {
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.11.0.tgz",
|
||||
"integrity": "sha512-t4M2bxMp2rKGK/l7bkVWjN+xVw9H9V12jAeXmO/Fskz2RcG1ZNLQnKSx/W/zCRMk8k7xOQFsfiApq+zDN+ziKA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/browser-utils": "10.11.0",
|
||||
"@sentry/core": "10.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/replay-canvas": {
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.11.0.tgz",
|
||||
"integrity": "sha512-brWQ90IYQyZr44IpTprlmvbtz4l2ABzLdpP94Egh12Onf/q6n4CjLKaA25N5kX0uggHqX1Rs7dNaG0mP3ETHhA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/replay": "10.11.0",
|
||||
"@sentry/core": "10.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.11.0.tgz",
|
||||
"integrity": "sha512-qemaKCJKJHHCyGBpdLq23xL5u9Xvir20XN7YFTnHcEq4Jvj0GoWsslxKi5cQB2JvpYn62WxTiDgVLeQlleZhSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/browser-utils": "10.11.0",
|
||||
"@sentry-internal/feedback": "10.11.0",
|
||||
"@sentry-internal/replay": "10.11.0",
|
||||
"@sentry-internal/replay-canvas": "10.11.0",
|
||||
"@sentry/core": "10.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.11.0.tgz",
|
||||
"integrity": "sha512-39Rxn8cDXConx3+SKOCAhW+/hklM7UDaz+U1OFzFMDlT59vXSpfI6bcXtNiFDrbOxlQ2hX8yAqx8YRltgSftoA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/vue": {
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/vue/-/vue-10.11.0.tgz",
|
||||
"integrity": "sha512-uXPXce4QCEuutG3b7FEcA+fhvXCgVPA/iFyeBMdagtjKGRLWgM0nRgVww/WGrltq8414aq7dAiBTWmPKAoaslw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/browser": "10.11.0",
|
||||
"@sentry/core": "10.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pinia": "2.x || 3.x",
|
||||
"vue": "2.x || 3.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pinia": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.34.41",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
|
||||
@@ -4157,9 +4262,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz",
|
||||
"integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==",
|
||||
"version": "24.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.3.tgz",
|
||||
"integrity": "sha512-GKBNHjoNw3Kra1Qg5UXttsY5kiWMEfoHq2TmXb+b1rcm6N7B3wTrFYIf/oSZ1xNQ+hVVijgLkiDZh7jRRsh+Gw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
11
package.json
11
package.json
@@ -36,10 +36,11 @@
|
||||
"@eslint/js": "^9.35.0",
|
||||
"@fontsource/noto-sans-jp": "^5.2.7",
|
||||
"@fontsource/noto-sans-kr": "^5.2.7",
|
||||
"@fontsource/noto-sans-sc": "^5.2.6",
|
||||
"@fontsource/noto-sans-sc": "^5.2.7",
|
||||
"@fontsource/noto-sans-tc": "^5.2.7",
|
||||
"@sentry/vue": "^10.11.0",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^24.3.1",
|
||||
"@types/node": "^24.3.3",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"animate.css": "^4.1.1",
|
||||
"babel-runtime": "^6.26.0",
|
||||
@@ -47,21 +48,21 @@
|
||||
"cross-env": "^10.0.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"echarts": "^6.0.0",
|
||||
"electron": "^37.4.0",
|
||||
"electron": "^37.5.0",
|
||||
"electron-builder": "^26.0.12",
|
||||
"element-plus": "^2.11.2",
|
||||
"esbuild-jest": "^0.4.0",
|
||||
"eslint": "^9.35.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-vue": "^9.33.0",
|
||||
"globals": "^16.3.0",
|
||||
"globals": "^16.4.0",
|
||||
"jest": "^30.1.3",
|
||||
"noty": "^3.2.0-beta-deprecated",
|
||||
"pinia": "^3.0.3",
|
||||
"prettier": "^3.6.2",
|
||||
"remixicon": "^4.6.0",
|
||||
"sass-embedded": "^1.92.1",
|
||||
"vite": "^7.1.4",
|
||||
"vite": "^7.1.5",
|
||||
"vue": "^3.5.21",
|
||||
"vue-i18n": "^11.1.12",
|
||||
"vue-marquee-text-component": "^2.0.1",
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
|
||||
import { createApp } from 'vue';
|
||||
import { pinia } from './stores';
|
||||
import { initPlugins } from './plugin';
|
||||
import { i18n } from './plugin/i18n';
|
||||
import { initComponents } from './plugin/components';
|
||||
import { initPlugins, i18n, initComponents, initSentry } from './plugin';
|
||||
import ElementPlus from 'element-plus';
|
||||
import App from './App.vue';
|
||||
|
||||
@@ -22,6 +20,7 @@ app.use(pinia);
|
||||
app.use(i18n);
|
||||
app.use(ElementPlus);
|
||||
initComponents(app);
|
||||
initSentry(app);
|
||||
|
||||
app.mount('#root');
|
||||
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
//
|
||||
|
||||
@use './assets/scss/flags.scss';
|
||||
@use './assets/scss/animated-emoji.scss';
|
||||
@use 'element-plus/theme-chalk/src/index.scss' as *;
|
||||
|
||||
@use '@fontsource/noto-sans-kr/korean.css';
|
||||
@use '@fontsource/noto-sans-jp/japanese.css';
|
||||
@use '@fontsource/noto-sans-sc/chinese-simplified.css';
|
||||
@@ -21,6 +17,10 @@
|
||||
@use '@fontsource/noto-sans-sc';
|
||||
@use '@fontsource/noto-sans-tc';
|
||||
|
||||
@use './assets/scss/flags.scss';
|
||||
@use './assets/scss/animated-emoji.scss';
|
||||
@use 'element-plus/theme-chalk/src/index.scss' as *;
|
||||
|
||||
@use 'element-plus/theme-chalk/src/dark/css-vars.scss';
|
||||
@use 'animate.css/animate.min.css';
|
||||
@use 'noty/lib/noty.css';
|
||||
|
||||
@@ -11,3 +11,7 @@ export async function initPlugins(isVrOverlay = false) {
|
||||
initDayjs();
|
||||
initNoty(isVrOverlay);
|
||||
}
|
||||
|
||||
export * from './i18n';
|
||||
export * from './components';
|
||||
export * from './sentry';
|
||||
|
||||
33
src/plugin/sentry.js
Normal file
33
src/plugin/sentry.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import * as Sentry from '@sentry/vue';
|
||||
import configRepository from '../service/config';
|
||||
|
||||
export function initSentry(app) {
|
||||
configRepository
|
||||
.getString('VRCX_SentryEnabled', 'false')
|
||||
.then((enabled) => {
|
||||
let isNightly = false;
|
||||
AppApi.GetVersion().then(
|
||||
(v) => (isNightly = v.includes('Nightly'))
|
||||
);
|
||||
if (enabled === 'true' && isNightly) {
|
||||
Sentry.init({
|
||||
app,
|
||||
dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
|
||||
environment: 'nightly',
|
||||
sampleRate: 0.1,
|
||||
beforeSend(event) {
|
||||
if (
|
||||
event.message &&
|
||||
(event.message.toLowerCase().includes('password') ||
|
||||
event.message.toLowerCase().includes('token'))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return event;
|
||||
},
|
||||
integrations: []
|
||||
});
|
||||
console.log('Sentry initialized');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -32,7 +32,10 @@ import { useVrcxStore } from './vrcx';
|
||||
import { useVRCXUpdaterStore } from './vrcxUpdater';
|
||||
import { useWorldStore } from './world';
|
||||
|
||||
import { createSentryPiniaPlugin } from '@sentry/vue';
|
||||
|
||||
export const pinia = createPinia();
|
||||
pinia.use(createSentryPiniaPlugin());
|
||||
|
||||
export function createGlobalStores() {
|
||||
return {
|
||||
|
||||
@@ -8,11 +8,13 @@ import webApiService from '../../service/webapi';
|
||||
import { watchState } from '../../service/watchState';
|
||||
import { useGameStore } from '../game';
|
||||
import { useVrcxStore } from '../vrcx';
|
||||
import { useVRCXUpdaterStore } from '../vrcxUpdater';
|
||||
import { AppDebug } from '../../service/appConfig';
|
||||
|
||||
export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
const gameStore = useGameStore();
|
||||
const vrcxStore = useVrcxStore();
|
||||
const VRCXUpdaterStore = useVRCXUpdaterStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -47,7 +49,8 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
isVRChatConfigDialogVisible: false,
|
||||
saveInstanceEmoji: false,
|
||||
vrcRegistryAutoBackup: true,
|
||||
vrcRegistryAskRestore: true
|
||||
vrcRegistryAskRestore: true,
|
||||
sentryErrorReporting: false
|
||||
});
|
||||
|
||||
async function initAdvancedSettings() {
|
||||
@@ -78,7 +81,8 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
notificationOpacity,
|
||||
saveInstanceEmoji,
|
||||
vrcRegistryAutoBackup,
|
||||
vrcRegistryAskRestore
|
||||
vrcRegistryAskRestore,
|
||||
sentryErrorReporting
|
||||
] = await Promise.all([
|
||||
configRepository.getBool('enablePrimaryPassword', false),
|
||||
configRepository.getBool('VRCX_relaunchVRChatAfterCrash', false),
|
||||
@@ -118,7 +122,8 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
configRepository.getFloat('VRCX_notificationOpacity', 100),
|
||||
configRepository.getBool('VRCX_saveInstanceEmoji', false),
|
||||
configRepository.getBool('VRCX_vrcRegistryAutoBackup', true),
|
||||
configRepository.getBool('VRCX_vrcRegistryAskRestore', true)
|
||||
configRepository.getBool('VRCX_vrcRegistryAskRestore', true),
|
||||
configRepository.getString('VRCX_SentryEnabled', 'false')
|
||||
]);
|
||||
|
||||
state.enablePrimaryPassword = enablePrimaryPassword;
|
||||
@@ -148,8 +153,18 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
state.saveInstanceEmoji = saveInstanceEmoji;
|
||||
state.vrcRegistryAutoBackup = vrcRegistryAutoBackup;
|
||||
state.vrcRegistryAskRestore = vrcRegistryAskRestore;
|
||||
state.sentryErrorReporting = sentryErrorReporting === 'true';
|
||||
|
||||
handleSetAppLauncherSettings();
|
||||
|
||||
setTimeout(() => {
|
||||
if (
|
||||
VRCXUpdaterStore.branch === 'Nightly' &&
|
||||
sentryErrorReporting === ''
|
||||
) {
|
||||
checkSentryConsent();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
initAdvancedSettings();
|
||||
@@ -225,6 +240,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
});
|
||||
const vrcRegistryAutoBackup = computed(() => state.vrcRegistryAutoBackup);
|
||||
const vrcRegistryAskRestore = computed(() => state.vrcRegistryAskRestore);
|
||||
const sentryErrorReporting = computed(() => state.sentryErrorReporting);
|
||||
|
||||
/**
|
||||
* @param {boolean} value
|
||||
@@ -422,6 +438,69 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
);
|
||||
}
|
||||
|
||||
async function checkSentryConsent() {
|
||||
ElMessageBox.confirm(
|
||||
'Help improve VRCX by allowing anonymous error reporting?\n\n' +
|
||||
'• Only collects crash and error information\n' +
|
||||
'• No personal data or VRChat information is collected\n' +
|
||||
'• Only enabled in nightly builds\n' +
|
||||
'• Can be disabled anytime in Advanced Settings',
|
||||
'Anonymous Error Reporting',
|
||||
{
|
||||
type: 'info',
|
||||
center: true
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
state.sentryErrorReporting = true;
|
||||
configRepository.setString('VRCX_SentryEnabled', 'true');
|
||||
|
||||
ElMessageBox.confirm(
|
||||
'Error reporting setting has been enabled. Would you like to restart VRCX now for the change to take effect?',
|
||||
'Restart Required',
|
||||
{
|
||||
confirmButtonText: 'Restart Now',
|
||||
cancelButtonText: 'Later',
|
||||
type: 'info',
|
||||
center: true
|
||||
}
|
||||
).then(() => {
|
||||
VRCXUpdaterStore.restartVRCX(false);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
state.sentryErrorReporting = false;
|
||||
configRepository.setString('VRCX_SentryEnabled', 'false');
|
||||
});
|
||||
}
|
||||
|
||||
async function setSentryErrorReporting() {
|
||||
if (VRCXUpdaterStore.branch !== 'Nightly') {
|
||||
return;
|
||||
}
|
||||
|
||||
state.sentryErrorReporting = !state.sentryErrorReporting;
|
||||
await configRepository.setString(
|
||||
'VRCX_SentryEnabled',
|
||||
state.sentryErrorReporting ? 'true' : 'false'
|
||||
);
|
||||
|
||||
ElMessageBox.confirm(
|
||||
'Error reporting setting has been disabled. Would you like to restart VRCX now for the change to take effect?',
|
||||
'Restart Required',
|
||||
{
|
||||
confirmButtonText: 'Restart Now',
|
||||
cancelButtonText: 'Later',
|
||||
type: 'info',
|
||||
center: true
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
VRCXUpdaterStore.restartVRCX(false);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function getSqliteTableSizes() {
|
||||
const [
|
||||
gps,
|
||||
@@ -726,6 +805,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
saveInstanceEmoji,
|
||||
vrcRegistryAutoBackup,
|
||||
vrcRegistryAskRestore,
|
||||
sentryErrorReporting,
|
||||
|
||||
setEnablePrimaryPasswordConfigRepository,
|
||||
setRelaunchVRChatAfterCrash,
|
||||
@@ -764,6 +844,8 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
setSaveInstanceEmoji,
|
||||
setVrcRegistryAutoBackup,
|
||||
setVrcRegistryAskRestore,
|
||||
setSentryErrorReporting,
|
||||
checkSentryConsent,
|
||||
askDeleteAllScreenshotMetadata
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1486,6 +1486,17 @@
|
||||
setSelfInviteOverride();
|
||||
saveOpenVROption();
|
||||
" />
|
||||
|
||||
<!--//- Sentry Error Reporting (Nightly Only)-->
|
||||
<div v-if="isNightlyBuild">
|
||||
<span class="sub-header">Anonymous Error Reporting (Nightly Only)</span>
|
||||
<simple-switch
|
||||
label="Help improve VRCX by sending anonymous error reports. Only collects crash and error information, no personal data or VRChat information is collected."
|
||||
:value="sentryErrorReporting"
|
||||
:long-label="true"
|
||||
@change="setSentryErrorReporting()" />
|
||||
</div>
|
||||
|
||||
<!--//- Advanced | Disable local world database-->
|
||||
</div>
|
||||
|
||||
@@ -2164,7 +2175,9 @@
|
||||
ugcFolderPath,
|
||||
notificationOpacity,
|
||||
autoDeleteOldPrints,
|
||||
saveInstanceEmoji
|
||||
saveInstanceEmoji,
|
||||
sentryErrorReporting,
|
||||
isNightlyBuild
|
||||
} = storeToRefs(advancedSettingsStore);
|
||||
|
||||
const {
|
||||
@@ -2192,6 +2205,7 @@
|
||||
showVRChatConfig,
|
||||
promptAutoClearVRCXCacheFrequency,
|
||||
setSaveInstanceEmoji,
|
||||
setSentryErrorReporting,
|
||||
askDeleteAllScreenshotMetadata
|
||||
} = advancedSettingsStore;
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
//
|
||||
|
||||
@use '../assets/scss/flags.scss';
|
||||
|
||||
@use '@fontsource/noto-sans-kr/korean.css';
|
||||
@use '@fontsource/noto-sans-jp/japanese.css';
|
||||
@use '@fontsource/noto-sans-sc/chinese-simplified.css';
|
||||
@@ -19,6 +17,8 @@
|
||||
@use '@fontsource/noto-sans-sc';
|
||||
@use '@fontsource/noto-sans-tc';
|
||||
|
||||
@use '../assets/scss/flags.scss';
|
||||
|
||||
@use 'animate.css/animate.min.css';
|
||||
@use 'noty/lib/noty.css';
|
||||
@use 'remixicon/fonts/remixicon.css';
|
||||
|
||||
Reference in New Issue
Block a user