lint: apply pretty import

This commit is contained in:
pa
2025-10-15 09:08:38 +09:00
committed by Natsumi
parent bed76e0ad8
commit afbb6dfa47
203 changed files with 3502 additions and 1441 deletions

View File

@@ -9,5 +9,9 @@
"omnisharp.useModernNet": false,
"[csharp]": {
"editor.defaultFormatter": "ms-dotnettools.csharp"
}
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": ["javascript", "vue", "html"]
}

View File

@@ -1,7 +1,10 @@
import { defineConfig } from 'eslint/config';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import globals from 'globals';
import js from '@eslint/js';
import pluginVue from 'eslint-plugin-vue';
import { defineConfig } from 'eslint/config';
import globals from 'globals';
import prettyImport from '@kamiya4047/eslint-plugin-pretty-import';
export default defineConfig([
{
@@ -69,5 +72,19 @@ export default defineConfig([
'vue/no-v-text-v-html-on-component': 'off',
'vue/no-use-v-if-with-v-for': 'warn'
}
}
},
{
plugins: { 'pretty-import': prettyImport },
rules: {
'pretty-import/separate-type-imports': 'warn',
'pretty-import/sort-import-groups': [
'warn',
{
groupStyleImports: true
}
],
'pretty-import/sort-import-names': 'warn'
}
},
eslintPluginPrettierRecommended
]);

2516
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,7 @@
"@fontsource/noto-sans-kr": "^5.2.8",
"@fontsource/noto-sans-sc": "^5.2.8",
"@fontsource/noto-sans-tc": "^5.2.8",
"@kamiya4047/eslint-plugin-pretty-import": "^0.1.6",
"@sentry/vite-plugin": "^4.4.0",
"@sentry/vue": "^10.19.0",
"@types/jest": "^30.0.0",
@@ -55,6 +56,7 @@
"esbuild-jest": "^0.5.0",
"eslint": "^9.37.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-vue": "^9.33.0",
"globals": "^16.4.0",
"jest": "^30.2.0",

View File

@@ -91,8 +91,11 @@
</template>
<script setup>
import './app.scss';
import { computed, onBeforeMount, onMounted } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import cs from 'element-plus/es/locale/lang/cs';
import en from 'element-plus/es/locale/lang/en';
import es from 'element-plus/es/locale/lang/es';
import fr from 'element-plus/es/locale/lang/fr';
@@ -101,55 +104,52 @@
import ko from 'element-plus/es/locale/lang/ko';
import pl from 'element-plus/es/locale/lang/pl';
import pt from 'element-plus/es/locale/lang/pt';
import cs from 'element-plus/es/locale/lang/cs';
import ru from 'element-plus/es/locale/lang/ru';
import th from 'element-plus/es/locale/lang/th';
import vi from 'element-plus/es/locale/lang/vi';
import zhCN from 'element-plus/es/locale/lang/zh-cn';
import zhTW from 'element-plus/es/locale/lang/zh-tw';
import th from 'element-plus/es/locale/lang/th';
import Login from './views/Login/Login.vue';
import NavMenu from './components/NavMenu.vue';
import MacOSTitleBar from './components/TitleBar/MacOSTitleBar.vue';
import Sidebar from './views/Sidebar/Sidebar.vue';
import Feed from './views/Feed/Feed.vue';
import GameLog from './views/GameLog/GameLog.vue';
import PlayerList from './views/PlayerList/PlayerList.vue';
import Search from './views/Search/Search.vue';
import Favorites from './views/Favorites/Favorites.vue';
import FriendLog from './views/FriendLog/FriendLog.vue';
import Moderation from './views/Moderation/Moderation.vue';
import Notification from './views/Notifications/Notification.vue';
import FriendList from './views/FriendList/FriendList.vue';
import Charts from './views/Charts/Charts.vue';
import Tools from './views/Tools/Tools.vue';
import Profile from './views/Profile/Profile.vue';
import Settings from './views/Settings/Settings.vue';
import { createGlobalStores, useAppearanceSettingsStore } from './stores';
import { initNoty } from './plugin/noty';
import { watchState } from './service/watchState';
import UserDialog from './components/dialogs/UserDialog/UserDialog.vue';
import WorldDialog from './components/dialogs/WorldDialog/WorldDialog.vue';
import AvatarDialog from './components/dialogs/AvatarDialog/AvatarDialog.vue';
import GroupDialog from './components/dialogs/GroupDialog/GroupDialog.vue';
import GroupMemberModerationDialog from './components/dialogs/GroupDialog/GroupMemberModerationDialog.vue';
import FullscreenImagePreview from './components/FullscreenImagePreview.vue';
import PreviousInstancesInfoDialog from './components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue';
import LaunchDialog from './components/dialogs/LaunchDialog.vue';
import LaunchOptionsDialog from './views/Settings/dialogs/LaunchOptionsDialog.vue';
import FriendImportDialog from './views/Favorites/dialogs/FriendImportDialog.vue';
import WorldImportDialog from './views/Favorites/dialogs/WorldImportDialog.vue';
import AvatarImportDialog from './views/Favorites/dialogs/AvatarImportDialog.vue';
import Charts from './views/Charts/Charts.vue';
import ChooseFavoriteGroupDialog from './components/dialogs/ChooseFavoriteGroupDialog.vue';
import EditInviteMessageDialog from './views/Profile/dialogs/EditInviteMessageDialog.vue';
import Favorites from './views/Favorites/Favorites.vue';
import Feed from './views/Feed/Feed.vue';
import FriendImportDialog from './views/Favorites/dialogs/FriendImportDialog.vue';
import FriendList from './views/FriendList/FriendList.vue';
import FriendLog from './views/FriendLog/FriendLog.vue';
import FullscreenImagePreview from './components/FullscreenImagePreview.vue';
import GameLog from './views/GameLog/GameLog.vue';
import GroupDialog from './components/dialogs/GroupDialog/GroupDialog.vue';
import GroupMemberModerationDialog from './components/dialogs/GroupDialog/GroupMemberModerationDialog.vue';
import LaunchDialog from './components/dialogs/LaunchDialog.vue';
import LaunchOptionsDialog from './views/Settings/dialogs/LaunchOptionsDialog.vue';
import Login from './views/Login/Login.vue';
import MacOSTitleBar from './components/TitleBar/MacOSTitleBar.vue';
import Moderation from './views/Moderation/Moderation.vue';
import NavMenu from './components/NavMenu.vue';
import Notification from './views/Notifications/Notification.vue';
import PlayerList from './views/PlayerList/PlayerList.vue';
import PreviousInstancesInfoDialog from './components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue';
import PrimaryPasswordDialog from './views/Settings/dialogs/PrimaryPasswordDialog.vue';
import Profile from './views/Profile/Profile.vue';
import Search from './views/Search/Search.vue';
import Settings from './views/Settings/Settings.vue';
import Sidebar from './views/Sidebar/Sidebar.vue';
import Tools from './views/Tools/Tools.vue';
import UserDialog from './components/dialogs/UserDialog/UserDialog.vue';
import VRCXUpdateDialog from './components/dialogs/VRCXUpdateDialog.vue';
import VRChatConfigDialog from './views/Settings/dialogs/VRChatConfigDialog.vue';
import PrimaryPasswordDialog from './views/Settings/dialogs/PrimaryPasswordDialog.vue';
import WorldDialog from './components/dialogs/WorldDialog/WorldDialog.vue';
import WorldImportDialog from './views/Favorites/dialogs/WorldImportDialog.vue';
import { onMounted, computed, onBeforeMount } from 'vue';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { createGlobalStores, useAppearanceSettingsStore } from './stores';
import { watchState } from './service/watchState';
import { initNoty } from './plugin/noty';
import './app.scss';
console.log(`isLinux: ${LINUX}`);

View File

@@ -1,5 +1,5 @@
import { request } from '../service/request';
import { useFavoriteStore, useUserStore } from '../stores';
import { request } from '../service/request';
function getCurrentUserId() {
return useUserStore().currentUser.id;

View File

@@ -1,5 +1,5 @@
import { useGroupStore, useUserStore } from '../stores';
import { request } from '../service/request';
import { useUserStore, useGroupStore } from '../stores';
function getCurrentUserId() {
return useUserStore().currentUser.id;

View File

@@ -1,5 +1,5 @@
import { request } from '../service/request';
import { useAvatarStore, useWorldStore } from '../stores';
import { request } from '../service/request';
const imageReq = {
async uploadAvatarFailCleanup(id) {
@@ -12,10 +12,12 @@ const imageReq = {
const fileVersion = json.versions[json.versions.length - 1].version;
request(`file/${fileId}/${fileVersion}/signature/finish`, {
method: 'PUT'
}).catch(err => console.error('Failed to finish signature:', err));
}).catch((err) =>
console.error('Failed to finish signature:', err)
);
request(`file/${fileId}/${fileVersion}/file/finish`, {
method: 'PUT'
}).catch(err => console.error('Failed to finish file:', err));
}).catch((err) => console.error('Failed to finish file:', err));
} catch (error) {
console.error('Failed to cleanup avatar upload:', error);
}
@@ -143,10 +145,12 @@ const imageReq = {
const fileVersion = json.versions[json.versions.length - 1].version;
request(`file/${fileId}/${fileVersion}/signature/finish`, {
method: 'PUT'
}).catch(err => console.error('Failed to finish signature:', err));
}).catch((err) =>
console.error('Failed to finish signature:', err)
);
request(`file/${fileId}/${fileVersion}/file/finish`, {
method: 'PUT'
}).catch(err => console.error('Failed to finish file:', err));
}).catch((err) => console.error('Failed to finish file:', err));
} catch (error) {
console.error('Failed to cleanup world upload:', error);
}

View File

@@ -4,24 +4,25 @@
*/
import { request } from '../service/request';
import userRequest from './user';
import worldRequest from './world';
import instanceRequest from './instance';
import friendRequest from './friend';
import avatarRequest from './avatar';
import notificationRequest from './notification';
import playerModerationRequest from './playerModeration';
import authRequest from './auth';
import avatarModerationRequest from './avatarModeration';
import avatarRequest from './avatar';
import favoriteRequest from './favorite';
import vrcPlusIconRequest from './vrcPlusIcon';
import vrcPlusImageRequest from './vrcPlusImage';
import friendRequest from './friend';
import groupRequest from './group';
import imageRequest from './image';
import instanceRequest from './instance';
import inventoryRequest from './inventory';
import inviteMessagesRequest from './inviteMessages';
import miscRequest from './misc';
import groupRequest from './group';
import authRequest from './auth';
import inventoryRequest from './inventory';
import notificationRequest from './notification';
import playerModerationRequest from './playerModeration';
import propRequest from './prop';
import imageRequest from './image';
import userRequest from './user';
import vrcPlusIconRequest from './vrcPlusIcon';
import vrcPlusImageRequest from './vrcPlusImage';
import worldRequest from './world';
window.request = {
request,

View File

@@ -1,4 +1,5 @@
import { ElMessage } from 'element-plus';
import { i18n } from '../plugin/i18n';
import { request } from '../service/request';
import { useInstanceStore } from '../stores';

View File

@@ -1,5 +1,5 @@
import { request } from '../service/request';
import { useGalleryStore, useNotificationStore } from '../stores';
import { request } from '../service/request';
/**
* @returns {any}

View File

@@ -5,9 +5,12 @@
// For a copy, see <https://opensource.org/licenses/MIT>.
import { createApp } from 'vue';
import { pinia } from './stores';
import { initPlugins, i18n, initComponents, initSentry } from './plugin';
import ElementPlus from 'element-plus';
import { i18n, initComponents, initPlugins, initSentry } from './plugin';
import { pinia } from './stores';
import App from './App.vue';
await initPlugins();

View File

@@ -8,6 +8,7 @@
<script setup>
import { ref, watch } from 'vue';
import { useAvatarStore } from '../stores';
const avatarStore = useAvatarStore();

View File

@@ -4,9 +4,11 @@
<script setup>
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import * as workerTimers from 'worker-timers';
import { timeToText } from '../shared/utils';
import * as workerTimers from 'worker-timers';
const props = defineProps({
datetime: { type: String, default: '' },
hours: { type: Number, default: 1 }

View File

@@ -27,7 +27,7 @@
</template>
<script>
import { computed, ref, watch, toRefs } from 'vue';
import { computed, ref, toRefs, watch } from 'vue';
export default {
name: 'DataTable',

View File

@@ -4,8 +4,9 @@
<script setup>
import { ref, watch } from 'vue';
import { userRequest } from '../api';
import { useUserStore } from '../stores';
import { userRequest } from '../api';
const userStore = useUserStore();

View File

@@ -58,12 +58,13 @@
</template>
<script setup>
import { WarningFilled, Close, Loading } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { Close, Loading, WarningFilled } from '@element-plus/icons-vue';
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { userImage, userStatusClass } from '../shared/utils';
import { useAppearanceSettingsStore, useFriendStore } from '../stores';
import { userImage, userStatusClass } from '../shared/utils';
const props = defineProps({
friend: { type: Object, required: true },

View File

@@ -36,17 +36,16 @@
</template>
<script setup>
import { CopyDocument, Download, RefreshLeft, RefreshRight, ZoomIn, ZoomOut } from '@element-plus/icons-vue';
import { nextTick, ref, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { Download, CopyDocument, ZoomIn, ZoomOut, RefreshRight, RefreshLeft } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import Noty from 'noty';
import { storeToRefs } from 'pinia';
import { escapeTag, extractFileId } from '../shared/utils';
import { useGalleryStore } from '../stores';
import { ref, nextTick, watch } from 'vue';
import { getNextDialogIndex } from '../shared/utils/base/ui';
import { useGalleryStore } from '../stores';
const galleryStore = useGalleryStore();
const { fullscreenImageDialog } = storeToRefs(galleryStore);

View File

@@ -60,12 +60,13 @@
<script setup>
import { ElMessage, ElMessageBox } from 'element-plus';
import { CaretBottom } from '@element-plus/icons-vue';
import { reactive, watch } from 'vue';
import { CaretBottom } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import { miscRequest } from '../api';
import { formatDateFilter, hasGroupPermission } from '../shared/utils';
import { useGroupStore, useInstanceStore, useLocationStore, useUserStore } from '../stores';
import { formatDateFilter, hasGroupPermission } from '../shared/utils';
import { miscRequest } from '../api';
const { t } = useI18n();

View File

@@ -18,14 +18,15 @@
</template>
<script setup>
import { ElMessage } from 'element-plus';
import { Loading, Message } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { instanceRequest } from '../api';
import { useI18n } from 'vue-i18n';
import { checkCanInviteSelf, parseLocation } from '../shared/utils';
import { useInviteStore, useLaunchStore } from '../stores';
import { instanceRequest } from '../api';
const props = defineProps({
location: String,

View File

@@ -10,10 +10,11 @@
</template>
<script setup>
import { ref, watch } from 'vue';
import { Location } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useInstanceStore } from '../stores';
const { instanceJoinHistory } = storeToRefs(useInstanceStore());

View File

@@ -10,6 +10,7 @@
import { SwitchButton } from '@element-plus/icons-vue';
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { checkCanInviteSelf } from '../shared/utils';
import { useLaunchStore } from '../stores';

View File

@@ -22,11 +22,13 @@
<script setup>
import { Loading, Lock, WarnTriangleFilled } from '@element-plus/icons-vue';
import { ref, watchEffect, watch } from 'vue';
import { ref, watch, watchEffect } from 'vue';
import { storeToRefs } from 'pinia';
import { getGroupName, getWorldName, parseLocation } from '../shared/utils';
import { useGroupStore, useInstanceStore, useSearchStore, useWorldStore } from '../stores';
import { useI18n } from 'vue-i18n';
import { useGroupStore, useInstanceStore, useSearchStore, useWorldStore } from '../stores';
import { getGroupName, getWorldName, parseLocation } from '../shared/utils';
const { t } = useI18n();
const { cachedWorlds, showWorldDialog } = useWorldStore();

View File

@@ -17,9 +17,11 @@
import { Lock, Unlock, WarnTriangleFilled } from '@element-plus/icons-vue';
import { ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { getGroupName, parseLocation } from '../shared/utils';
import { useGroupStore, useLaunchStore, useInstanceStore } from '../stores';
import { useI18n } from 'vue-i18n';
import { useGroupStore, useInstanceStore, useLaunchStore } from '../stores';
import { getGroupName, parseLocation } from '../shared/utils';
const { t } = useI18n();
const { cachedInstances } = useInstanceStore();
const { lastInstanceApplied } = storeToRefs(useInstanceStore());

View File

@@ -41,6 +41,7 @@
import { Download } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useUiStore, useVRCXUpdaterStore } from '../stores';
const { t } = useI18n();

View File

@@ -3,6 +3,7 @@
</template>
<script setup>
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
import { timeToText } from '../shared/utils';
const props = defineProps({

View File

@@ -571,50 +571,49 @@
</template>
<script setup>
import { ElMessage, ElMessageBox } from 'element-plus';
import {
Delete,
Star,
StarFilled,
Back,
Check,
MoreFilled,
Refresh,
Share,
CircleCheck,
CircleClose,
Picture,
User,
Edit,
Download,
Upload,
Back,
Right,
CopyDocument,
Delete,
Download,
Edit,
MoreFilled,
Picture,
Refresh,
Right,
Share,
Star,
StarFilled,
Upload,
User,
Warning
} from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { computed, defineAsyncComponent, nextTick, reactive, ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { avatarModerationRequest, avatarRequest, favoriteRequest, miscRequest } from '../../../api';
import { database } from '../../../service/database';
import {
buildTreeData,
commaNumber,
copyToClipboard,
downloadAndSaveJson,
extractFileId,
formatDateFilter,
moveArrayItem,
openExternalLink,
openFolderGeneric,
replaceVrcPackageUrl,
timeToText,
moveArrayItem,
formatDateFilter
timeToText
} from '../../../shared/utils';
import { handleImageUploadInput } from '../../../shared/utils/imageUpload';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
import { useAvatarStore, useFavoriteStore, useGalleryStore, useGameStore, useUserStore } from '../../../stores';
import { avatarModerationRequest, avatarRequest, favoriteRequest, miscRequest } from '../../../api';
import { database } from '../../../service/database';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
import { handleImageUploadInput } from '../../../shared/utils/imageUpload';
const ChangeAvatarImageDialog = defineAsyncComponent(() => import('./ChangeAvatarImageDialog.vue'));
const SetAvatarStylesDialog = defineAsyncComponent(() => import('./SetAvatarStylesDialog.vue'));

View File

@@ -44,15 +44,16 @@
<script setup>
import { ElMessage } from 'element-plus';
import { Upload } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { avatarRequest, imageRequest } from '../../../api';
import { handleImageUploadInput } from '../../../shared/utils/imageUpload';
import { useAvatarStore } from '../../../stores';
import { $throw } from '../../../service/request';
import { AppDebug } from '../../../service/appConfig';
import { extractFileId } from '../../../shared/utils';
import { handleImageUploadInput } from '../../../shared/utils/imageUpload';
import { useAvatarStore } from '../../../stores';
const { t } = useI18n();

View File

@@ -60,13 +60,13 @@
</template>
<script setup>
import { ElMessage } from 'element-plus';
import { useI18n } from 'vue-i18n';
import { watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { arraysMatch } from '../../../shared/utils';
import { avatarRequest } from '../../../api';
import { useAvatarStore } from '../../../stores';
import { ElMessage } from 'element-plus';
const { t } = useI18n();
const { applyAvatar } = useAvatarStore();

View File

@@ -92,11 +92,11 @@
</template>
<script setup>
import { Loading } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { watch } from 'vue';
import { Loading } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import { watch } from 'vue';
import { avatarRequest } from '../../../api';
import { useAvatarStore } from '../../../stores';

View File

@@ -62,15 +62,16 @@
</template>
<script setup>
import { computed, nextTick, ref, watch } from 'vue';
import { Check } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import Noty from 'noty';
import { storeToRefs } from 'pinia';
import { computed, nextTick, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useFavoriteStore, useUserStore } from '../../stores';
import { favoriteRequest } from '../../api';
import { getNextDialogIndex } from '../../shared/utils/base/ui';
import { useFavoriteStore, useUserStore } from '../../stores';
const { t } = useI18n();

View File

@@ -53,14 +53,13 @@
</template>
<script setup>
import { ElMessage } from 'element-plus';
import { Close, Refresh, Upload } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { vrcPlusImageRequest } from '../../../api';
import { useGalleryStore, useUserStore } from '../../../stores';
import { vrcPlusImageRequest } from '../../../api';
const { t } = useI18n();

View File

@@ -1121,61 +1121,62 @@
</template>
<script setup>
import { ElMessage, ElMessageBox } from 'element-plus';
import {
ArrowDown,
ChatLineSquare,
Check,
View,
Star,
StarFilled,
Close,
Message,
MoreFilled,
Refresh,
Share,
Tickets,
Delete,
CircleCheck,
CircleClose,
Edit,
CopyDocument,
Download,
Operation,
Loading,
Warning,
ArrowDown,
Close,
CollectionTag,
ChatLineSquare
CopyDocument,
Delete,
Download,
Edit,
Loading,
Message,
MoreFilled,
Operation,
Refresh,
Share,
Star,
StarFilled,
Tickets,
View,
Warning
} from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { nextTick, reactive, ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import * as workerTimers from 'worker-timers';
import { groupRequest } from '../../../api';
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
import {
buildTreeData,
copyToClipboard,
debounce,
downloadAndSaveJson,
formatDateFilter,
getFaviconUrl,
hasGroupPermission,
hasGroupModerationPermission,
hasGroupPermission,
languageClass,
openExternalLink,
refreshInstancePlayerCount,
removeFromArray,
userImage,
userStatusClass,
formatDateFilter,
debounce
userStatusClass
} from '../../../shared/utils';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
import { useGalleryStore, useGroupStore, useLocationStore, useUserStore } from '../../../stores';
import InviteGroupDialog from '../InviteGroupDialog.vue';
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
import { groupRequest } from '../../../api';
import GroupPostEditDialog from './GroupPostEditDialog.vue';
import InviteGroupDialog from '../InviteGroupDialog.vue';
import PreviousInstancesGroupDialog from '../PreviousInstancesDialog/PreviousInstancesGroupDialog.vue';
import * as workerTimers from 'worker-timers';
const { t } = useI18n();
const { showUserDialog } = useUserStore();

View File

@@ -861,19 +861,21 @@
</template>
<script setup>
import { Refresh, Delete, ArrowDown, Warning, Loading } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { storeToRefs } from 'pinia';
import { ArrowDown, Delete, Loading, Refresh, Warning } from '@element-plus/icons-vue';
import { reactive, ref, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import * as workerTimers from 'worker-timers';
import { groupRequest, userRequest } from '../../../api';
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
import { hasGroupPermission, userImage, userImageFull, formatDateFilter, debounce } from '../../../shared/utils';
import { debounce, formatDateFilter, hasGroupPermission, userImage, userImageFull } from '../../../shared/utils';
import { useAppearanceSettingsStore, useGalleryStore, useGroupStore, useUserStore } from '../../../stores';
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
import { groupRequest, userRequest } from '../../../api';
import GroupMemberModerationExportDialog from './GroupMemberModerationExportDialog.vue';
import * as workerTimers from 'worker-timers';
const { randomUserColours } = storeToRefs(useAppearanceSettingsStore());
const { showUserDialog } = useUserStore();
const { currentUser } = storeToRefs(useUserStore());

View File

@@ -32,6 +32,7 @@
<script setup>
import { ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { copyToClipboard } from '../../../shared/utils';
const { t } = useI18n();

View File

@@ -98,11 +98,13 @@
</template>
<script setup>
import { computed, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { groupRequest, vrcPlusIconRequest } from '../../../api';
import { useGalleryStore, useGroupStore } from '../../../stores';
import GallerySelectDialog from './GallerySelectDialog.vue';
const props = defineProps({

View File

@@ -35,9 +35,10 @@
import { ElMessage } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { instanceRequest, inviteMessagesRequest, notificationRequest } from '../../../api';
import { parseLocation } from '../../../shared/utils';
import { useGalleryStore, useUserStore } from '../../../stores';
import { parseLocation } from '../../../shared/utils';
const { t } = useI18n();
const { uploadImage } = storeToRefs(useGalleryStore());

View File

@@ -169,13 +169,14 @@
<script setup>
import { ElMessage, ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { instanceRequest, notificationRequest } from '../../../api';
import { parseLocation, userImage, userStatusClass } from '../../../shared/utils';
import { useFriendStore, useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
import { parseLocation, userImage, userStatusClass } from '../../../shared/utils';
import { instanceRequest, notificationRequest } from '../../../api';
import SendInviteDialog from './SendInviteDialog.vue';
const { vipFriends, onlineFriends, activeFriends } = storeToRefs(useFriendStore());

View File

@@ -25,9 +25,10 @@
import { ElMessage } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { instanceRequest, notificationRequest } from '../../../api';
import { parseLocation } from '../../../shared/utils';
import { useGalleryStore, useUserStore } from '../../../stores';
import { parseLocation } from '../../../shared/utils';
const { t } = useI18n();

View File

@@ -91,11 +91,12 @@
<script setup>
import { Edit } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
import EditAndSendInviteDialog from './EditAndSendInviteDialog.vue';
import SendInviteConfirmDialog from './SendInviteConfirmDialog.vue';

View File

@@ -165,15 +165,17 @@
</template>
<script setup>
import { computed, nextTick, ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { ref, watch, nextTick, computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { groupRequest, userRequest } from '../../api';
import configRepository from '../../service/config';
import { hasGroupPermission, userImage, userStatusClass } from '../../shared/utils';
import { getNextDialogIndex } from '../../shared/utils/base/ui';
import { groupRequest, userRequest } from '../../api';
import { useFriendStore, useGroupStore } from '../../stores';
import { getNextDialogIndex } from '../../shared/utils/base/ui';
import configRepository from '../../service/config';
const { vipFriends, onlineFriends, activeFriends, offlineFriends } = storeToRefs(useFriendStore());
const { currentUserGroups, inviteGroupDialog } = storeToRefs(useGroupStore());

View File

@@ -97,18 +97,19 @@
</template>
<script setup>
import { computed, nextTick, ref, watch } from 'vue';
import { CopyDocument, Warning } from '@element-plus/icons-vue';
import { ref, computed, nextTick, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { instanceRequest, worldRequest } from '../../api';
import configRepository from '../../service/config';
import { useFriendStore, useGameStore, useInviteStore, useLaunchStore, useLocationStore } from '../../stores';
import { checkCanInvite, getLaunchURL, isRealInstance, parseLocation } from '../../shared/utils';
import { instanceRequest, worldRequest } from '../../api';
import { getNextDialogIndex } from '../../shared/utils/base/ui';
import { useFriendStore, useInviteStore, useLaunchStore, useLocationStore, useGameStore } from '../../stores';
import InviteDialog from './InviteDialog/InviteDialog.vue';
import configRepository from '../../service/config';
const { t } = useI18n();

View File

@@ -60,9 +60,10 @@
</template>
<script setup>
import { ref, watch, nextTick, computed } from 'vue';
import { computed, nextTick, ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { groupRequest, userRequest } from '../../api';
import { hasGroupModerationPermission, userImage } from '../../shared/utils';
import { getNextDialogIndex } from '../../shared/utils/base/ui';

View File

@@ -473,12 +473,11 @@
</template>
<script setup>
import { ref, watch, nextTick, computed } from 'vue';
import { computed, nextTick, ref, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { groupRequest, instanceRequest, worldRequest } from '../../api';
import configRepository from '../../service/config';
import { useI18n } from 'vue-i18n';
import {
copyToClipboard,
getLaunchURL,
@@ -488,17 +487,20 @@
userImage,
userStatusClass
} from '../../shared/utils';
import { getNextDialogIndex } from '../../shared/utils/base/ui';
import {
useFriendStore,
useGroupStore,
useInstanceStore,
useInviteStore,
useLaunchStore,
useLocationStore,
useUserStore,
useInviteStore
useUserStore
} from '../../stores';
import { groupRequest, instanceRequest, worldRequest } from '../../api';
import { getNextDialogIndex } from '../../shared/utils/base/ui';
import InviteDialog from './InviteDialog/InviteDialog.vue';
import configRepository from '../../service/config';
const props = defineProps({
newInstanceDialogLocationTag: {

View File

@@ -62,21 +62,21 @@
</template>
<script setup>
import { DataLine, Close } from '@element-plus/icons-vue';
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { Close, DataLine } from '@element-plus/icons-vue';
import { ElMessageBox } from 'element-plus';
import { ref, reactive, computed, watch, nextTick } from 'vue';
import {
parseLocation,
compareByCreatedAt,
timeToText,
removeFromArray,
formatDateFilter
} from '../../../shared/utils';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
import { database } from '../../../service/database';
import { useI18n } from 'vue-i18n';
import {
compareByCreatedAt,
formatDateFilter,
parseLocation,
removeFromArray,
timeToText
} from '../../../shared/utils';
import { useInstanceStore, useUiStore } from '../../../stores';
import { database } from '../../../service/database';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
const { showPreviousInstancesInfoDialog } = useInstanceStore();
const { shiftHeld } = useUiStore();

View File

@@ -59,13 +59,14 @@
</template>
<script setup>
import { ref, watch, nextTick } from 'vue';
import { nextTick, ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { database } from '../../../service/database';
import { compareByCreatedAt, parseLocation, timeToText, formatDateFilter } from '../../../shared/utils';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
import { compareByCreatedAt, formatDateFilter, parseLocation, timeToText } from '../../../shared/utils';
import { useGameLogStore, useInstanceStore, useUserStore } from '../../../stores';
import { database } from '../../../service/database';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
const { lookupUser } = useUserStore();
const { previousInstancesInfoDialogVisible, previousInstancesInfoDialogInstanceId } =

View File

@@ -69,22 +69,22 @@
</template>
<script setup>
import { DataLine, Close } from '@element-plus/icons-vue';
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { Close, DataLine } from '@element-plus/icons-vue';
import { ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { database } from '../../../service/database';
import {
compareByCreatedAt,
formatDateFilter,
parseLocation,
removeFromArray,
timeToText,
formatDateFilter
timeToText
} from '../../../shared/utils';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
import { useInstanceStore, useUiStore, useUserStore } from '../../../stores';
import { database } from '../../../service/database';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
const { t } = useI18n();

View File

@@ -47,13 +47,12 @@
</template>
<script setup>
import { ElMessage } from 'element-plus';
import { Delete } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { useI18n } from 'vue-i18n';
import { userRequest } from '../../../api';
import { getFaviconUrl } from '../../../shared/utils';
import { userRequest } from '../../../api';
const { t } = useI18n();
const props = defineProps({

View File

@@ -45,9 +45,10 @@
<script setup>
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { userRequest } from '../../../api';
import { languageClass } from '../../../shared/utils';
import { useUserStore } from '../../../stores';
import { userRequest } from '../../../api';
const { t } = useI18n();

View File

@@ -72,22 +72,22 @@
</template>
<script setup>
import { SwitchButton, DataLine, Close } from '@element-plus/icons-vue';
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { Close, DataLine, SwitchButton } from '@element-plus/icons-vue';
import { ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { database } from '../../../service/database';
import {
compareByCreatedAt,
formatDateFilter,
parseLocation,
removeFromArray,
timeToText,
formatDateFilter
timeToText
} from '../../../shared/utils';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
import { useInstanceStore, useLaunchStore, useUiStore } from '../../../stores';
import { database } from '../../../service/database';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
const props = defineProps({
previousInstancesUserDialog: {

View File

@@ -27,6 +27,7 @@
<script setup>
import { ElMessage } from 'element-plus';
import { useI18n } from 'vue-i18n';
import { userRequest } from '../../../api';
const { t } = useI18n();

View File

@@ -64,11 +64,12 @@
<script setup>
import { Edit } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
import EditAndSendInviteDialog from '../InviteDialog/EditAndSendInviteDialog.vue';
import SendInviteConfirmDialog from '../InviteDialog/SendInviteConfirmDialog.vue';

View File

@@ -58,11 +58,11 @@
<script setup>
import { ElMessage } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { userRequest } from '../../../api';
import { useUserStore } from '../../../stores';
import { userRequest } from '../../../api';
const { t } = useI18n();
const { currentUser } = storeToRefs(useUserStore());

View File

@@ -1690,72 +1690,59 @@
</template>
<script setup>
import { ElMessage, ElMessageBox } from 'element-plus';
import {
Delete,
ArrowDown,
Bottom,
CaretBottom,
ChatDotRound,
ChatLineRound,
Check,
Star,
StarFilled,
MoreFilled,
Refresh,
Share,
Picture,
Edit,
SwitchButton,
Message,
Close,
Plus,
DataLine,
CircleCheck,
CircleClose,
User,
Flag,
CopyDocument,
Setting,
Download,
CaretBottom,
UserFilled,
Postcard,
Operation,
Microphone,
Mute,
ChatLineRound,
ChatDotRound,
Pointer,
Loading,
More,
Warning,
ArrowDown,
Top,
Bottom,
Close,
CollectionTag,
View
CopyDocument,
DataLine,
Delete,
Download,
Edit,
Flag,
Loading,
Message,
Microphone,
More,
MoreFilled,
Mute,
Operation,
Picture,
Plus,
Pointer,
Postcard,
Refresh,
Setting,
Share,
Star,
StarFilled,
SwitchButton,
Top,
User,
UserFilled,
View,
Warning
} from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import {
favoriteRequest,
friendRequest,
groupRequest,
miscRequest,
notificationRequest,
playerModerationRequest,
userRequest,
worldRequest
} from '../../../api';
import { database } from '../../../service/database';
import { processBulk, request } from '../../../service/request';
import { userDialogGroupSortingOptions } from '../../../shared/constants';
import { userDialogWorldOrderOptions, userDialogWorldSortingOptions } from '../../../shared/constants/';
import {
checkCanInvite,
compareByMemberCount,
compareByName,
copyToClipboard,
downloadAndSaveJson,
formatDateFilter,
getFaviconUrl,
isFriendOnline,
isRealInstance,
languageClass,
@@ -1768,11 +1755,8 @@
userImage,
userOnlineFor,
userOnlineForTimestamp,
userStatusClass,
formatDateFilter,
getFaviconUrl
userStatusClass
} from '../../../shared/utils';
import { getNextDialogIndex, redirectToToolsTab } from '../../../shared/utils/base/ui';
import {
useAdvancedSettingsStore,
useAppearanceSettingsStore,
@@ -1786,12 +1770,28 @@
useInviteStore,
useLocationStore,
useModerationStore,
useUiStore,
useUserStore,
useWorldStore,
useUiStore
useWorldStore
} from '../../../stores';
import SendInviteDialog from '../InviteDialog/SendInviteDialog.vue';
import {
favoriteRequest,
friendRequest,
groupRequest,
miscRequest,
notificationRequest,
playerModerationRequest,
userRequest,
worldRequest
} from '../../../api';
import { getNextDialogIndex, redirectToToolsTab } from '../../../shared/utils/base/ui';
import { processBulk, request } from '../../../service/request';
import { userDialogWorldOrderOptions, userDialogWorldSortingOptions } from '../../../shared/constants/';
import { database } from '../../../service/database';
import { userDialogGroupSortingOptions } from '../../../shared/constants';
import InviteGroupDialog from '../InviteGroupDialog.vue';
import SendInviteDialog from '../InviteDialog/SendInviteDialog.vue';
const BioDialog = defineAsyncComponent(() => import('./BioDialog.vue'));
const LanguageDialog = defineAsyncComponent(() => import('./LanguageDialog.vue'));

View File

@@ -58,9 +58,10 @@
</template>
<script setup>
import { storeToRefs } from 'pinia';
import { nextTick, ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { branches } from '../../shared/constants';
import { getNextDialogIndex } from '../../shared/utils/base/ui';
import { useVRCXUpdaterStore } from '../../stores';

View File

@@ -44,15 +44,16 @@
<script setup>
import { ElMessage } from 'element-plus';
import { Upload } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { worldRequest, imageRequest } from '../../../api';
import { handleImageUploadInput } from '../../../shared/utils/imageUpload';
import { useWorldStore } from '../../../stores';
import { imageRequest, worldRequest } from '../../../api';
import { $throw } from '../../../service/request';
import { AppDebug } from '../../../service/appConfig';
import { extractFileId } from '../../../shared/utils';
import { handleImageUploadInput } from '../../../shared/utils/imageUpload';
import { useWorldStore } from '../../../stores';
const { t } = useI18n();

View File

@@ -85,11 +85,12 @@
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { computed, ref, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { useI18n } from 'vue-i18n';
import { worldRequest } from '../../../api';
import { useWorldStore } from '../../../stores';
import { worldRequest } from '../../../api';
const props = defineProps({
oldTags: {

View File

@@ -27,12 +27,11 @@
</template>
<script setup>
import { computed, ref, watch } from 'vue';
import { Delete } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { ref, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { worldRequest } from '../../../api';
const props = defineProps({

View File

@@ -746,55 +746,51 @@
<script setup>
import {
HomeFilled,
ArrowDown,
Check,
CopyDocument,
DataLine,
Delete,
Download,
Edit,
Flag,
HomeFilled,
Loading,
MagicStick,
Message,
MoreFilled,
Picture,
Refresh,
Share,
Flag,
Message,
MagicStick,
Picture,
Upload,
Edit,
Download,
View,
DataLine,
CopyDocument,
Warning,
Star,
StarFilled,
Upload,
User,
Check,
Loading,
ArrowDown,
UserFilled
UserFilled,
View,
Warning
} from '@element-plus/icons-vue';
import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { computed, ref, watch, nextTick, defineAsyncComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { favoriteRequest, miscRequest, userRequest, worldRequest } from '../../../api';
import { database } from '../../../service/database.js';
import {
buildTreeData,
commaNumber,
copyToClipboard,
deleteVRChatCache,
downloadAndSaveJson,
formatDateFilter,
openExternalLink,
openFolderGeneric,
refreshInstancePlayerCount,
replaceVrcPackageUrl,
textToHex,
timeToText,
userImage,
userStatusClass,
openFolderGeneric,
deleteVRChatCache,
commaNumber,
formatDateFilter,
textToHex,
copyToClipboard
userStatusClass
} from '../../../shared/utils';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
import {
useAppearanceSettingsStore,
useFavoriteStore,
@@ -806,6 +802,9 @@
useUserStore,
useWorldStore
} from '../../../stores';
import { favoriteRequest, miscRequest, userRequest, worldRequest } from '../../../api';
import { database } from '../../../service/database.js';
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
const NewInstanceDialog = defineAsyncComponent(() => import('../NewInstanceDialog.vue'));
const PreviousInstancesWorldDialog = defineAsyncComponent(

View File

@@ -1,33 +1,41 @@
class InteropApi {
constructor() {
return new Proxy(this, {
get(target, prop) {
if (WINDOWS) {
return undefined;
}
// If the property is not a method of InteropApi,
// treat it as a .NET class name
if (typeof prop === 'string' && !target[prop]) {
return new Proxy({}, {
get(_, methodName) {
// Return a method that calls the .NET method dynamically
return async (...args) => {
return await target.callMethod(prop, methodName, ...args);
};
}
});
}
return target[prop];
}
});
return new Proxy(this, {
get(target, prop) {
if (WINDOWS) {
return undefined;
}
// If the property is not a method of InteropApi,
// treat it as a .NET class name
if (typeof prop === 'string' && !target[prop]) {
return new Proxy(
{},
{
get(_, methodName) {
// Return a method that calls the .NET method dynamically
return async (...args) => {
return await target.callMethod(
prop,
methodName,
...args
);
};
}
}
);
}
return target[prop];
}
});
}
async callMethod(className, methodName, ...args) {
return window.interopApi.callDotNetMethod(className, methodName, args)
.then(result => {
return result;
});
return window.interopApi
.callDotNetMethod(className, methodName, args)
.then((result) => {
return result;
});
}
}
export default new InteropApi();
export default new InteropApi();

View File

@@ -1,24 +1,28 @@
// Because this isn't a package (just a loose js file), we have to use require
// statements
const process = require("node:process")
const process = require('node:process');
const fs = require('node:fs');
const path = require('node:path');
const yargs = require("yargs/yargs");
const { hideBin } = require("yargs/helpers")
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const getLocalizationObjects = function* () {
const localeFolder = './src/localization';
const folders = fs.readdirSync(localeFolder, { withFileTypes: true }).filter(file => file.isDirectory());
const folders = fs
.readdirSync(localeFolder, { withFileTypes: true })
.filter((file) => file.isDirectory());
for (const folder of folders) {
const filePath = path.join(localeFolder, folder.name, "en.json");
const filePath = path.join(localeFolder, folder.name, 'en.json');
const jsonStr = fs.readFileSync(filePath);
yield [filePath, JSON.parse(jsonStr)];
}
}
};
const addKey = function (obj, objects, value, above_key) {
console.log(`Adding key to ${obj.language} at path '${objects.join('.')}' with value '${value}' above key '${above_key}'`);
console.log(
`Adding key to ${obj.language} at path '${objects.join('.')}' with value '${value}' above key '${above_key}'`
);
let currentObj = obj;
let i = 0;
@@ -32,12 +36,12 @@ const addKey = function (obj, objects, value, above_key) {
currentObj = currentObj[objects[i]];
}
InsertKeyInObj(currentObj, objects[i], value, above_key);
}
};
// Shamelessly stolen from https://stackoverflow.com/a/55017155/11030436
const InsertKeyInObj = (obj, key, value, above_key) => {
const keys = Object.keys(obj);
if (keys.length === 0 || !(Object.hasOwn(obj, above_key))) {
if (keys.length === 0 || !Object.hasOwn(obj, above_key)) {
obj[key] = value;
return obj;
}
@@ -58,7 +62,7 @@ const InsertKeyInObj = (obj, key, value, above_key) => {
}
return newObj;
}, {})
}, {});
delete ret.dummy;
// Clear keys on old object
@@ -68,7 +72,7 @@ const InsertKeyInObj = (obj, key, value, above_key) => {
// Assign new properties to old object
Object.assign(obj, ret);
}
};
const addLocalizationKey = (key, value, above_key) => {
const objects = key.split('.');
@@ -79,11 +83,13 @@ const addLocalizationKey = (key, value, above_key) => {
}
console.log(`\`${key}:${value}\` added to every localization file!`);
}
};
const removeKey = (obj, objects, i = 0) => {
console.log(`Removing key from ${obj.language} at path '${objects.join('.')}'`);
if (!(Object.hasOwn(obj, objects[i]))) {
console.log(
`Removing key from ${obj.language} at path '${objects.join('.')}'`
);
if (!Object.hasOwn(obj, objects[i])) {
return;
}
@@ -95,7 +101,7 @@ const removeKey = (obj, objects, i = 0) => {
delete obj[objects[i]];
}
}
}
};
const removeLocalizationKey = (key) => {
const objects = key.split('.');
@@ -108,12 +114,14 @@ const removeLocalizationKey = (key) => {
}
console.log(`\`${key}\` removed from every localization file!`);
}
};
// Yes this code is extremely slow, but it doesn't run very often so.
const Validate = function () {
const files = [...getLocalizationObjects()];
const enIndex = files.findIndex(file => path.dirname(file[0]).endsWith("en"));
const enIndex = files.findIndex((file) =>
path.dirname(file[0]).endsWith('en')
);
const [_, enObj] = files.splice(enIndex, 1)[0];
const traverse = function (obj, predicate, pathes = []) {
@@ -124,16 +132,16 @@ const Validate = function () {
traverse(obj[key], predicate, [...pathes, key]);
}
}
}
};
let hasRemoved = false;
for (const [_, localeObj] of files) {
toRemove = []
toRemove = [];
traverse(localeObj, (_, key, pathes) => {
let currObj = enObj;
for (const pathSegment of pathes) {
if (Object.hasOwn(currObj, pathSegment)) {
currObj = currObj[pathSegment]
currObj = currObj[pathSegment];
} else {
toRemove.push([...pathes, key]);
return;
@@ -151,10 +159,14 @@ const Validate = function () {
}
}
toAdd = []
toAdd = [];
traverse(enObj, (obj, key, pathes) => {
// Add above_key to the toAdd entry
if (toAdd.length > 0 && typeof toAdd.at(-1)[3] === 'undefined' && toAdd.at(-1)[1].at(-2) === pathes.at(-1)) {
if (
toAdd.length > 0 &&
typeof toAdd.at(-1)[3] === 'undefined' &&
toAdd.at(-1)[1].at(-2) === pathes.at(-1)
) {
toAdd.at(-1)[3] = key;
}
@@ -164,7 +176,12 @@ const Validate = function () {
if (Object.hasOwn(currObj, pathSegment)) {
currObj = currObj[pathSegment];
} else {
toAdd.push([localeObj, [...pathes, key], obj[key], undefined]);
toAdd.push([
localeObj,
[...pathes, key],
obj[key],
undefined
]);
return;
}
}
@@ -181,20 +198,23 @@ const Validate = function () {
if (toAdd.length > 0 || hasRemoved) {
for (const [localePath, localeObj] of files) {
fs.writeFileSync(localePath, `${JSON.stringify(localeObj, null, 4)}\n`);
fs.writeFileSync(
localePath,
`${JSON.stringify(localeObj, null, 4)}\n`
);
}
} else {
console.log("validation passed!");
console.log('validation passed!');
}
}
};
const cliParser = yargs(hideBin(process.argv))
.command({
command: 'add <key> <value> [above_key]',
aliases: ['a', 'replace', 'r'],
desc: 'adds or replaces a key and value to all localization files above `above_key`',
handler: (argv) => addLocalizationKey(argv.key, argv.value, argv.above_key)
handler: (argv) =>
addLocalizationKey(argv.key, argv.value, argv.above_key)
})
.command({
command: 'remove <key>',
@@ -205,17 +225,20 @@ const cliParser = yargs(hideBin(process.argv))
.command({
command: 'validate',
aliases: [],
desc: 'removes keys from other languages that don\'t exist in the en translation and adds keys that don\'t exist in other languages',
desc: "removes keys from other languages that don't exist in the en translation and adds keys that don't exist in other languages",
handler: Validate
})
.demandCommand(1)
.example([
['$0 add foo.bar "I\'m adding a key!"', 'Adding a key as `foo.bar`'],
['$0 remove foo.bar', 'removes the foo.bar key'],
['$0 add foo.bar "I\'m adding a key!" baz', 'Adding a key aboe the existing `foo.baz` key']
[
'$0 add foo.bar "I\'m adding a key!" baz',
'Adding a key aboe the existing `foo.baz` key'
]
])
.help(false)
.version(false)
.version(false);
cliParser
.wrap(cliParser.terminalWidth())
@@ -226,4 +249,4 @@ cliParser
handler: () => cliParser.showHelp()
})
.fail(() => cliParser.showHelp())
.parse()
.parse();

View File

@@ -1,5 +1,6 @@
import AvatarInfo from '../components/AvatarInfo.vue';
import CountdownTimer from '../components/CountdownTimer.vue';
import DataTable from '../components/DataTable.vue';
import DisplayName from '../components/DisplayName.vue';
import InstanceInfo from '../components/InstanceInfo.vue';
import InviteYourself from '../components/InviteYourself.vue';
@@ -8,7 +9,6 @@ import Launch from '../components/Launch.vue';
import Location from '../components/Location.vue';
import LocationWorld from '../components/LocationWorld.vue';
import Timer from '../components/Timer.vue';
import DataTable from '../components/DataTable.vue';
export function initComponents(app) {
app.component('Location', Location);

View File

@@ -1,10 +1,10 @@
import customParseFormat from 'dayjs/plugin/customParseFormat';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import customParseFormat from 'dayjs/plugin/customParseFormat';
export function initDayjs() {
dayjs.extend(duration);

View File

@@ -1,4 +1,5 @@
import { createI18n } from 'vue-i18n';
import * as localizedStrings from '../localization/localizedStrings';
const i18n = createI18n({

View File

@@ -1,5 +1,5 @@
import { initInteropApi } from './interopApi';
import { initDayjs } from './dayjs';
import { initInteropApi } from './interopApi';
import { initNoty } from './noty';
import { initUi } from './ui';

View File

@@ -1,6 +1,7 @@
import * as Sentry from '@sentry/vue';
import configRepository from '../service/config';
import * as Sentry from '@sentry/vue';
export async function initSentry(app) {
const enabled = await configRepository.getString(
'VRCX_SentryEnabled',

View File

@@ -1,14 +1,15 @@
import configRepository from '../service/config';
import {
changeAppDarkStyle,
changeAppThemeStyle,
getThemeMode,
refreshCustomCss,
refreshCustomScript,
setLoginContainerStyle,
getThemeMode
setLoginContainerStyle
} from '../shared/utils/base/ui';
import { i18n } from './i18n';
import configRepository from '../service/config';
export async function initUi() {
try {
// @ts-ignore

View File

@@ -1,5 +1,7 @@
import { reactive } from 'vue';
import dayjs from 'dayjs';
import * as utils from '../shared/utils';
const AppDebug = reactive({

View File

@@ -31,36 +31,81 @@ const charToConfusables = new Map([
['pi', 'ᒆ'],
['Nj', 'Nj'],
['AE', 'ᴁ'],
['A', '𝑨𝔄ᗄ𝖠𝗔ꓯ𝞐🄐🄰Ꭿ𐊠𝕬𝜜𝐴ꓮᎪ𝚨ꭺ𝝖🅐Å∀🇦₳🅰𝒜𝘈𝐀𝔸дǺᗅⒶAΑᾋᗩĂÃÅǍȀȂĀȺĄʌΛλƛᴀᴬДАልÄₐᕱªǞӒΆẠẢẦẨẬẮẰẲẴẶᾸᾹᾺΆᾼᾈᾉᾊᾌᾍᾎᾏἈἉἊἋἌἍἎἏḀȦǠӐÀÁÂẤẪ𝛢𝓐𝙰𝘼ᗩ'],
['a', '∂⍺ⓐձǟᵃᶏ⒜аɒaαȃȁคǎმäɑāɐąᾄẚạảǡầẵḁȧӑӓãåάὰάăẩằẳặᾀᾁᾂᾃᾅᾆᾰᾱᾲᾳᾴᶐᾶᾷἀἁἂἃἄἅἆἇᾇậắàáâấẫǻⱥ𝐚𝑎𝒂𝒶𝓪𝔞𝕒𝖆𝖺𝗮𝘢𝙖𝚊𝛂𝛼𝜶𝝰𝞪⍶'],
['B', '🄑𝔙𝖁ꞵ𝛃𝛽𝜷𝝱𝞫Ᏸ𐌁𝑩𝕭🄱𐊡𝖡𝘽ꓐ𝗕𝘉𝜝𐊂𝚩𝐁𝛣𝝗𝐵𝙱𝔹Ᏼᏼ𝞑Ꞵ𝔅🅑฿𝓑ᗿᗾᗽ🅱ⒷBвϐᗷƁ乃ßცჩ๖βɮБՅ๒ᙖʙᴮᵇጌḄℬΒВẞḂḆɃദᗹᗸᵝᙞᙟᙝᛒᙗᙘᴃ🇧'],
[
'A',
'𝑨𝔄ᗄ𝖠𝗔ꓯ𝞐🄐🄰Ꭿ𐊠𝕬𝜜𝐴ꓮᎪ𝚨ꭺ𝝖🅐Å∀🇦₳🅰𝒜𝘈𝐀𝔸дǺᗅⒶAΑᾋᗩĂÃÅǍȀȂĀȺĄʌΛλƛᴀᴬДАልÄₐᕱªǞӒΆẠẢẦẨẬẮẰẲẴẶᾸᾹᾺΆᾼᾈᾉᾊᾌᾍᾎᾏἈἉἊἋἌἍἎἏḀȦǠӐÀÁÂẤẪ𝛢𝓐𝙰𝘼ᗩ'
],
[
'a',
'∂⍺ⓐձǟᵃᶏ⒜аɒaαȃȁคǎმäɑāɐąᾄẚạảǡầẵḁȧӑӓãåάὰάăẩằẳặᾀᾁᾂᾃᾅᾆᾰᾱᾲᾳᾴᶐᾶᾷἀἁἂἃἄἅἆἇᾇậắàáâấẫǻⱥ𝐚𝑎𝒂𝒶𝓪𝔞𝕒𝖆𝖺𝗮𝘢𝙖𝚊𝛂𝛼𝜶𝝰𝞪⍶'
],
[
'B',
'🄑𝔙𝖁ꞵ𝛃𝛽𝜷𝝱𝞫Ᏸ𐌁𝑩𝕭🄱𐊡𝖡𝘽ꓐ𝗕𝘉𝜝𐊂𝚩𝐁𝛣𝝗𝐵𝙱𝔹Ᏼᏼ𝞑Ꞵ𝔅🅑฿𝓑ᗿᗾᗽ🅱ⒷBвϐᗷƁ乃ßცჩ๖βɮБՅ๒ᙖʙᴮᵇጌḄℬΒВẞḂḆɃദᗹᗸᵝᙞᙟᙝᛒᙗᙘᴃ🇧'
],
['b', 'ꮟᏏ𝐛𝘣𝒷𝔟𝓫𝖇𝖻𝑏𝙗𝕓𝒃𝗯𝚋♭ᑳᒈbᖚᕹᕺⓑḃḅҍъḇƃɓƅᖯƄЬᑲþƂ⒝ЪᶀᑿᒀᒂᒁᑾьƀҌѢѣᔎ'],
['C', 'ꞆႠ℃🄒ᏟⲤ🄲ꓚ𐊢𐌂🅲𐐕🅒☾ČÇⒸCↃƇᑕㄈ¢८↻ĈϾՇȻᙅᶜ⒞ĆҀĊ©टƆℂℭϹС匚ḈҪʗᑖᑡᑢᑣᑤᑥⅭ𝐂𝐶𝑪𝒞𝓒𝕮𝖢𝗖𝘊𝘾ᔍ'],
['c', '🝌cⅽ𝐜𝑐𝒄𝒸𝓬𝔠𝕔𝖈𝖼𝗰𝘤𝙘𝚌ᴄϲⲥсꮯ𐐽ⲥ𐐽ꮯĉcⓒćčċçҁƈḉȼↄсርᴄϲҫ꒝ςɽϛ𝙲ᑦ᧚𝐜𝑐𝒄𝒸𝓬𝔠𝕔𝖈𝖼𝗰𝘤𝙘𝚌₵🇨ᥴᒼⅽ'],
[
'C',
'ꞆႠ℃🄒ᏟⲤ🄲ꓚ𐊢𐌂🅲𐐕🅒☾ČÇⒸCↃƇᑕㄈ¢८↻ĈϾՇȻᙅᶜ⒞ĆҀĊ©टƆℂℭϹС匚ḈҪʗᑖᑡᑢᑣᑤᑥⅭ𝐂𝐶𝑪𝒞𝓒𝕮𝖢𝗖𝘊𝘾ᔍ'
],
[
'c',
'🝌cⅽ𝐜𝑐𝒄𝒸𝓬𝔠𝕔𝖈𝖼𝗰𝘤𝙘𝚌ᴄϲⲥсꮯ𐐽ⲥ𐐽ꮯĉcⓒćčċçҁƈḉȼↄсርᴄϲҫ꒝ςɽϛ𝙲ᑦ᧚𝐜𝑐𝒄𝒸𝓬𝔠𝕔𝖈𝖼𝗰𝘤𝙘𝚌₵🇨ᥴᒼⅽ'
],
['D', '🄓Ꭰ🄳𝔡𝖉𝔻𝗗𝘋𝙳𝐷𝓓𝐃𝑫𝕯𝖣𝔇𝘿ꭰⅅ𝒟ꓓ🅳🅓ⒹDƉᗪƊÐԺᴅᴰↁḊĐÞⅮᗞᑯĎḌḐḒḎᗫᗬᗟᗠᶛᴆ🇩'],
['d', 'Ꮷ𝔡𝖉ᑯꓒ𝓭ᵭ₫ԃⓓdḋďḍḑḓḏđƌɖɗᵈ⒟ԁⅾᶁԀᑺᑻᑼᑽᒄᑰᑱᶑ𝕕𝖽𝑑𝘥𝒅𝙙𝐝𝗱𝚍ⅆ𝒹ʠժ'],
['E', '£ᙓ⋿∃ⴺꓱ𝐄𝐸𝔈𝕰𝖤𝘌𝙴𝛦𝜠ꭼ🄔🄴𝙀𝔼𐊆𝚬ꓰ𝝚𝞔𝓔𝑬𝗘🅴🅔ⒺΈEƎἝᕮƐモЄᴇᴱᵉÉ乇ЁɆꂅ€ÈℰΕЕⴹᎬĒĔĖĘĚÊËԐỀẾỄỂẼḔḖẺȄȆẸỆȨḜḘḚἘἙἚἛἜῈΈӖὲέЀϵ🇪'],
['e', 'əәⅇꬲꞓ⋴𝛆𝛜𝜀𝜖𝜺𝝐𝝴𝞊𝞮𝟄ⲉꮛ𐐩ꞒⲈ⍷𝑒𝓮𝕖𝖊𝘦𝗲𝚎𝙚𝒆𝔢𝖾𝐞Ҿҿⓔe⒠èᧉéᶒêɘἔềếễ૯ǝєεēҽɛểẽḕḗĕėëẻěȅȇẹệȩɇₑęḝḙḛ℮еԑѐӗᥱёἐἑἒἓἕℯ'],
[
'E',
'£ᙓ⋿∃ⴺꓱ𝐄𝐸𝔈𝕰𝖤𝘌𝙴𝛦𝜠ꭼ🄔🄴𝙀𝔼𐊆𝚬ꓰ𝝚𝞔𝓔𝑬𝗘🅴🅔ⒺΈEƎἝᕮƐモЄᴇᴱᵉÉ乇ЁɆꂅ€ÈℰΕЕⴹᎬĒĔĖĘĚÊËԐỀẾỄỂẼḔḖẺȄȆẸỆȨḜḘḚἘἙἚἛἜῈΈӖὲέЀϵ🇪'
],
[
'e',
'əәⅇꬲꞓ⋴𝛆𝛜𝜀𝜖𝜺𝝐𝝴𝞊𝞮𝟄ⲉꮛ𐐩ꞒⲈ⍷𝑒𝓮𝕖𝖊𝘦𝗲𝚎𝙚𝒆𝔢𝖾𝐞Ҿҿⓔe⒠èᧉéᶒêɘἔềếễ૯ǝєεēҽɛểẽḕḗĕėëẻěȅȇẹệȩɇₑęḝḙḛ℮еԑѐӗᥱёἐἑἒἓἕℯ'
],
['F', 'ᖵꘘꓞꟻᖷ𝐅𝐹𝑭𝔽𝕱𝖥𝗙𝙁𝙵𝟊℉🄕🄵𐊇𝔉𝘍𐊥ꓝꞘ🅵🅕𝓕ⒻFғҒᖴƑԲϝቻḞℱϜ₣🇫Ⅎ'],
['f', '𝐟ᵮ𝑓𝒇𝒻𝓯𝔣𝕗𝖿𝗳𝙛𝚏ꬵꞙẝ𝖋ⓕfƒḟʃբᶠ⒡ſꊰʄ∱ᶂ𝘧'],
['G', '𝗚𝘎🄖ꓖᏳ🄶Ꮐᏻ𝔾𝓖𝑮𝕲ꮐ𝒢𝙂𝖦𝙶𝔊𝐺𝐆🅶🅖ⒼGɢƓʛĢᘜᴳǴĠԌĜḠĞǦǤԍ₲🇬⅁'],
['g', 'ᶃᶢⓖgǵĝḡğġǧģց૭ǥɠﻭﻮᵍ⒢ℊɡᧁ𝐠𝑔𝒈𝓰𝔤𝕘𝖌𝗀𝗴𝘨𝙜𝚐'],
['H', 'Ἤ🄗𝆦🄷𝜢ꓧ𝘏𝐻𝝜𝖧𐋏𝗛ꮋℍᎻℌⲎ𝑯𝞖🅷🅗ዞǶԋⒽHĤᚺḢḦȞḤḨḪĦⱧҢңҤῊΉῌἨἩἪἫἭἮἯᾘᾙᾚᾛᾜᾝᾞᾟӉӈҥΉн卄♓𝓗ℋН𝐇𝙃𝙷ʜ𝛨Η𝚮ᕼӇᴴᵸ🇭'],
[
'H',
'Ἤ🄗𝆦🄷𝜢ꓧ𝘏𝐻𝝜𝖧𐋏𝗛ꮋℍᎻℌⲎ𝑯𝞖🅷🅗ዞǶԋⒽHĤᚺḢḦȞḤḨḪĦⱧҢңҤῊΉῌἨἩἪἫἭἮἯᾘᾙᾚᾛᾜᾝᾞᾟӉӈҥΉн卄♓𝓗ℋН𝐇𝙃𝙷ʜ𝛨Η𝚮ᕼӇᴴᵸ🇭'
],
['h', 'ꞕ৸𝕳ꚕᏲℏӊԊꜧᏂҺ⒣ђⓗhĥḣḧȟḥḩḫẖħⱨհһከኩኪካɦℎ𝐡𝒉𝒽𝓱𝔥𝕙𝖍𝗁𝗵𝘩𝙝𝚑իʰᑋᗁɧんɥ'],
['I', 'ⲒἿ🄘🄸ЇꀤᏆ🅸🅘إﺇٳأﺃٲٵⒾI៸ÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗェエῘῙῚΊἸἹἺἻἼἽἾⅠΪΊɪᶦᑊᥣ𝛪𝐈𝙄𝙸𝓵𝙡𝐼ᴵ𝚰𝑰🇮'],
['i', '⍳ℹⅈ𝑖𝒊𝒾ı𝚤ɩιιͺ𝛊𝜄𝜾𝞲ꙇӏꭵᎥⓘiìíîĩīĭïḯỉǐȉȋịḭῐῑῒΐῖῗἰἱἲⅰⅼ∣ⵏ│׀ا١۱ߊᛁἳἴἵɨіὶίᶖ𝔦𝚒𝝸𝗂𝐢𝕚𝖎𝗶𝘪𝙞ίⁱᵢ𝓲⒤'],
[
'I',
'ⲒἿ🄘🄸ЇꀤᏆ🅸🅘إﺇٳأﺃٲٵⒾI៸ÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗェエῘῙῚΊἸἹἺἻἼἽἾⅠΪΊɪᶦᑊᥣ𝛪𝐈𝙄𝙸𝓵𝙡𝐼ᴵ𝚰𝑰🇮'
],
[
'i',
'⍳ℹⅈ𝑖𝒊𝒾ı𝚤ɩιιͺ𝛊𝜄𝜾𝞲ꙇӏꭵᎥⓘiìíîĩīĭïḯỉǐȉȋịḭῐῑῒΐῖῗἰἱἲⅰⅼ∣ⵏ│׀ا١۱ߊᛁἳἴἵɨіὶίᶖ𝔦𝚒𝝸𝗂𝐢𝕚𝖎𝗶𝘪𝙞ίⁱᵢ𝓲⒤'
],
['J', '𝐉𝐽𝑱𝒥𝓙𝔍𝕁𝕵𝖩𝗝𝘑𝙅𝙹ꞲͿꓙ🄙🄹🅹🅙ⒿJЈʝᒍנフĴʆวلյʖᴊᴶﻝጋɈⱼՂๅႱįᎫȷ丿ℐℑᒘᒙᒚᒛᒴᒵᒎᒏ🇯'],
['j', '𝚥ꭻⅉⓙjϳʲ⒥ɉĵǰјڶᶨ𝒿𝘫𝗷𝑗𝙟𝔧𝒋𝗃𝓳𝕛𝚓𝖏𝐣'],
['K', '𝐊ꝄꝀ𝐾𝑲𝓚𝕶𝖪𝙺𝚱𝝟🄚𝗞🄺𝜥𝘒ꓗ𝙆𝕂Ⲕ𝔎𝛫Ꮶ𝞙𝒦🅺🅚₭ⓀKĸḰќƘкҠκқҟӄʞҚКҡᴋᴷᵏ⒦ᛕЌጕḲΚKҜҝҞĶḴǨⱩϗӃ🇰'],
['k', 'ⓚꝁkḱǩḳķḵƙⱪᶄ𝐤𝘬𝗄𝕜𝜅𝜘𝜿𝝒𝝹𝞌𝞳𝙠𝚔𝑘𝒌ϰ𝛋𝛞𝟆𝗸𝓴𝓀'],
['L', '𝐋𝐿𝔏𝕃𝕷𝖫𝗟𝘓𝙇ﴼ🄛🄻𐐛Ⳑ𝑳𝙻𐑃𝓛ⳑꮮᏞꓡ🅻🅛ﺈ└ⓁւLĿᒪ乚ՆʟꓶιԼᴸˡĹረḶₗΓլĻᄂⅬℒⱢᥧᥨᒻᒶᒷᶫﺎᒺᒹᒸᒫ⎳ㄥŁⱠﺄȽ🇱'],
[
'L',
'𝐋𝐿𝔏𝕃𝕷𝖫𝗟𝘓𝙇ﴼ🄛🄻𐐛Ⳑ𝑳𝙻𐑃𝓛ⳑꮮᏞꓡ🅻🅛ﺈ└ⓁւLĿᒪ乚ՆʟꓶιԼᴸˡĹረḶₗΓլĻᄂⅬℒⱢᥧᥨᒻᒶᒷᶫﺎᒺᒹᒸᒫ⎳ㄥŁⱠﺄȽ🇱'
],
['l', 'ⓛlŀĺľḷḹļӀℓḽḻłレɭƚɫⱡ|Ɩ⒧ʅǀוןΙІ|ᶩӏ𝓘𝕀𝖨𝗜𝘐𝐥𝑙𝒍𝓁𝔩𝕝𝖑𝗅𝗹𝘭𝚕𝜤𝝞ı𝚤ɩι𝛊𝜄𝜾𝞲'],
['M', 'ꮇ🄜🄼𐌑𐊰ꓟⲘᎷ🅼🅜ⓂMмṂ൱ᗰ州ᘻო๓♏ʍᙏᴍᴹᵐ⒨ḾМṀ௱ⅯℳΜϺᛖӍӎ𝐌𝑀𝑴𝓜𝔐𝕄𝕸𝖬𝗠𝘔𝙈𝙼𝚳𝛭𝜧𝝡𝞛🇲'],
['m', '₥ᵯ𝖒𝐦𝗆𝔪𝕞𝓂ⓜmനᙢ൩ḿṁⅿϻṃጠɱ៳ᶆ𝙢𝓶𝚖𝑚𝗺᧕᧗'],
['N', '𝇙𝇚𝇜🄝𝆧𝙉🄽ℕꓠ𝛮𝝢𝙽𝚴𝑵𝑁Ⲛ𝐍𝒩𝞜𝗡𝘕𝜨𝓝𝖭🅽₦🅝ЙЍⓃҋ៷NᴎɴƝᑎ几иՈռИהЛπᴺᶰŃ刀ክṄⁿÑПΝᴨոϖǸŇṆŅṊṈทŊӢӣӤӥћѝйᥢҊᴻ🇳'],
['n', 'ոռח𝒏𝓷𝙣𝑛𝖓𝔫𝗇𝚗𝗻ᥒⓝήnǹᴒńñᾗηṅňṇɲņṋṉղຖՌƞŋ⒩ภกɳпʼnлԉȠἠἡῃդᾐᾑᾒᾓᾔᾕᾖῄῆῇῂἢἣἤἥἦἧὴήበቡቢባቤብቦȵ𝛈𝜂𝜼𝝶𝞰𝕟𝘯𝐧𝓃ᶇᵰᥥ∩'],
['O', '𝜽⭘🔿ꭴ⭕⏺🄁🄀Ꭴ𝚯𝚹𝛩𝛳𝜣𝜭𝝝𝝧𝞗𝞡ⴱᎾᏫ⍬𝞱𝝷𝛉𝟎𝜃θ𝟘𝑂𝑶𝓞𝔒𝕆𝕺𝗢𝘖𝙊𝛰㈇ꄲ🄞🔾🄾𐊒𝟬ꓳⲞ𐐄𐊫𐓂𝞞🅞⍥◯ⵁ⊖0⊝𝝤Ѳϴ𝚶𝜪ѺӦӨӪΌʘ𝐎ǑÒŎÓÔÕȌȎㇿ❍ⓄOὋロ❤૦⊕ØФԾΘƠᴼᵒ⒪ŐÖₒ¤◊Φ〇ΟОՕଠഠ௦סỒỐỖỔṌȬṎŌṐṒȮȰȪỎỜỚỠỞỢỌỘǪǬǾƟⵔ߀៰⍜⎔⎕⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿⧀⧁⧂⧃ὈὉὊὌὍ'],
['o', 'ంಂംං૦௦۵ℴ𝑜𝒐𝖔ꬽ𝝄𝛔𝜎𝝈𝞂ჿ𝚘০୦ዐ𝛐𝗈𝞼ဝⲟ𝙤၀𐐬𝔬𐓪𝓸🇴⍤○ϙ🅾𝒪𝖮𝟢𝟶𝙾𝘰𝗼𝕠𝜊𝐨𝝾𝞸ᐤⓞѳ᧐ᥲðoఠᦞՓòөӧóºōôǒȏŏồốȍỗổõσṍȭṏὄṑṓȯȫ๏ᴏőöѻоዐǭȱ০୦٥౦೦൦๐໐οօᴑ०੦ỏơờớỡởợọộǫøǿɵծὀὁόὸόὂὃὅ'],
[
'N',
'𝇙𝇚𝇜🄝𝆧𝙉🄽ℕꓠ𝛮𝝢𝙽𝚴𝑵𝑁Ⲛ𝐍𝒩𝞜𝗡𝘕𝜨𝓝𝖭🅽₦🅝ЙЍⓃҋ៷NᴎɴƝᑎ几иՈռИהЛπᴺᶰŃ刀ክṄⁿÑПΝᴨոϖǸŇṆŅṊṈทŊӢӣӤӥћѝйᥢҊᴻ🇳'
],
[
'n',
'ոռח𝒏𝓷𝙣𝑛𝖓𝔫𝗇𝚗𝗻ᥒⓝήnǹᴒńñᾗηṅňṇɲņṋṉղຖՌƞŋ⒩ภกɳпʼnлԉȠἠἡῃդᾐᾑᾒᾓᾔᾕᾖῄῆῇῂἢἣἤἥἦἧὴήበቡቢባቤብቦȵ𝛈𝜂𝜼𝝶𝞰𝕟𝘯𝐧𝓃ᶇᵰᥥ∩'
],
[
'O',
'𝜽⭘🔿ꭴ⭕⏺🄁🄀Ꭴ𝚯𝚹𝛩𝛳𝜣𝜭𝝝𝝧𝞗𝞡ⴱᎾᏫ⍬𝞱𝝷𝛉𝟎𝜃θ𝟘𝑂𝑶𝓞𝔒𝕆𝕺𝗢𝘖𝙊𝛰㈇ꄲ🄞🔾🄾𐊒𝟬ꓳⲞ𐐄𐊫𐓂𝞞🅞⍥◯ⵁ⊖0⊝𝝤Ѳϴ𝚶𝜪ѺӦӨӪΌʘ𝐎ǑÒŎÓÔÕȌȎㇿ❍ⓄOὋロ❤૦⊕ØФԾΘƠᴼᵒ⒪ŐÖₒ¤◊Φ〇ΟОՕଠഠ௦סỒỐỖỔṌȬṎŌṐṒȮȰȪỎỜỚỠỞỢỌỘǪǬǾƟⵔ߀៰⍜⎔⎕⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿⧀⧁⧂⧃ὈὉὊὌὍ'
],
[
'o',
'ంಂംං૦௦۵ℴ𝑜𝒐𝖔ꬽ𝝄𝛔𝜎𝝈𝞂ჿ𝚘০୦ዐ𝛐𝗈𝞼ဝⲟ𝙤၀𐐬𝔬𐓪𝓸🇴⍤○ϙ🅾𝒪𝖮𝟢𝟶𝙾𝘰𝗼𝕠𝜊𝐨𝝾𝞸ᐤⓞѳ᧐ᥲðoఠᦞՓòөӧóºōôǒȏŏồốȍỗổõσṍȭṏὄṑṓȯȫ๏ᴏőöѻоዐǭȱ০୦٥౦೦൦๐໐οօᴑ०੦ỏơờớỡởợọộǫøǿɵծὀὁόὸόὂὃὅ'
],
['P', '🄟🄿ꓑ𝚸𝙿𝞠𝙋ꮲⲢ𝒫𝝦𝑃𝑷𝗣𝐏𐊕𝜬𝘗𝓟𝖯𝛲Ꮲ🅟Ҏ🅿ⓅPƤᑭ尸Ṗրφքᴘᴾᵖ⒫ṔアקРየᴩⱣℙΡῬᑸᑶᑷᑹᑬᑮ🇵₱'],
['p', 'ⲣҏ℗ⓟpṕṗƥᵽῥρрƿǷῤ⍴𝓹𝓅𝐩𝑝𝒑𝔭𝕡𝖕𝗉𝗽𝘱𝙥𝚙𝛒𝝆𝞺𝜌𝞀'],
['Q', '🅀🄠Ꝗ🆀🅠ⓆQℚⵕԚ𝐐𝑄𝑸𝒬𝓠𝚀𝘘𝙌𝖰𝕼𝔔𝗤🇶'],
@@ -69,26 +114,43 @@ const charToConfusables = new Map([
['r', '𝚛ꭇᣴℾ𝚪𝛤𝜞𝝘𝞒ⲄГᎱᒥꭈⲅꮁⓡrŕṙřȑȓṛṝŗгՐɾᥬṟɍʳ⒭ɼѓᴦᶉ𝐫𝑟𝒓𝓇𝓻𝔯𝕣𝖗𝗋𝗿𝘳𝙧ᵲґᵣ'],
['S', '🅂🄪🄢ꇙ𝓢𝗦Ꮪ𝒮Ꮥ𝚂𝐒ꓢ𝖲𝔖𝙎𐊖𝕾𐐠𝘚𝕊𝑆𝑺🆂🅢ⓈSṨŞֆՏȘˢ⒮ЅṠŠŚṤŜṦṢടᔕᔖᔢᔡᔣᔤ'],
['s', 'ᣵⓢꜱ𐑈ꮪsśṥŝṡšṧʂṣṩѕşșȿᶊక𝐬𝑠𝒔𝓈𝓼𝔰𝕤𝖘𝗌𝘀𝘴𝙨𝚜ގ🇸'],
['T', '🅃🄣七ፒ𝜯🆃𐌕𝚻𝛵𝕋𝕿𝑻𐊱𐊗𝖳𝙏🝨𝝩𝞣𝚃𝘛𝑇ꓔ⟙𝐓Ⲧ𝗧⊤𝔗Ꭲꭲ𝒯🅣⏇⏉ⓉTтҬҭƬイŦԵτᴛᵀイፕϮŤ⊥ƮΤТ下ṪṬȚŢṰṮ丅丁ᐪ𝛕𝜏𝝉𝞃𝞽𝓣ㄒ🇹ጥ'],
[
'T',
'🅃🄣七ፒ𝜯🆃𐌕𝚻𝛵𝕋𝕿𝑻𐊱𐊗𝖳𝙏🝨𝝩𝞣𝚃𝘛𝑇ꓔ⟙𝐓Ⲧ𝗧⊤𝔗Ꭲꭲ𝒯🅣⏇⏉ⓉTтҬҭƬイŦԵτᴛᵀイፕϮŤ⊥ƮΤТ下ṪṬȚŢṰṮ丅丁ᐪ𝛕𝜏𝝉𝞃𝞽𝓣ㄒ🇹ጥ'
],
['t', 'ⓣtṫẗťṭțȶ੮էʇ†ţṱṯƭŧᵗ⒯ʈեƫ𝐭𝑡𝒕𝓉𝓽𝔱𝕥𝖙𝗍𝘁𝘵𝙩𝚝ナ'],
['U', '🅄Џ🄤ሀꓴ𐓎꒤🆄🅤ŨŬŮᑗᑘǓǕǗǙⓊUȖᑌ凵ƱմԱꓵЦŪՄƲᙀᵁᵘ⒰ŰપÜՍÙÚÛṸṺǛỦȔƯỪỨỮỬỰỤṲŲṶṴɄᥩᑧ∪ᘮ⋃𝐔𝑈𝑼𝒰𝓤𝔘𝕌𝖀𝖴𝗨𝘜𝙐𝚄🇺'],
['u', '𝘂𝘶𝙪𝚞ꞟꭎꭒ𝛖𝜐𝝊𝞄𝞾𐓶ὺύⓤuùũūừṷṹŭǖữᥙǚǜὗυΰนսʊǘǔúůᴜűųยûṻцሁüᵾᵤµʋủȕȗưứửựụṳṵʉῠῡῢΰῦῧὐὑϋύὒὓὔὕὖᥔ𝐮𝑢𝒖𝓊𝓾𝔲𝕦𝖚𝗎ᶙ'],
[
'U',
'🅄Џ🄤ሀꓴ𐓎꒤🆄🅤ŨŬŮᑗᑘǓǕǗǙⓊUȖᑌ凵ƱմԱꓵЦŪՄƲᙀᵁᵘ⒰ŰપÜՍÙÚÛṸṺǛỦȔƯỪỨỮỬỰỤṲŲṶṴɄᥩᑧ∪ᘮ⋃𝐔𝑈𝑼𝒰𝓤𝔘𝕌𝖀𝖴𝗨𝘜𝙐𝚄🇺'
],
[
'u',
'𝘂𝘶𝙪𝚞ꞟꭎꭒ𝛖𝜐𝝊𝞄𝞾𐓶ὺύⓤuùũūừṷṹŭǖữᥙǚǜὗυΰนսʊǘǔúůᴜűųยûṻцሁüᵾᵤµʋủȕȗưứửựụṳṵʉῠῡῢΰῦῧὐὑϋύὒὓὔὕὖᥔ𝐮𝑢𝒖𝓊𝓾𝔲𝕦𝖚𝗎ᶙ'
],
['V', '𝑉𝒱𝕍𝗩🄥🅅ꓦ𝑽𝖵𝘝Ꮩ𝚅𝙑𝐕🆅🅥ⓋVᐯѴᵛ⒱۷ṾⅴⅤṼ٧ⴸѶᐺᐻ🇻𝓥'],
['v', '∨⌄⋁ⅴ𝐯𝑣𝒗𝓋𝔳𝕧𝖛𝗏ꮩሀⓥv𝜐𝝊ṽṿ౮งѵעᴠνטᵥѷ៴ᘁ𝙫𝚟𝛎𝜈𝝂𝝼𝞶𝘷𝘃𝓿'],
['W', '𝐖𝑊𝓦𝔚𝕎𝖂𝖶𝗪𝙒𝚆🄦🅆ᏔᎳ𝑾ꓪ𝒲𝘞🆆Ⓦ🅦wWẂᾧᗯᥕ山ѠຟచաЩШώщฬшᙎᵂʷ⒲ฝሠẄԜẀŴẆẈധᘺѿᙡƜ₩🇼'],
['w', '𝐰ꝡ𝑤𝒘𝓌𝔀𝔴𝕨𝖜𝗐𝘄𝘸𝙬𝚠աẁꮃẃⓦ⍵ŵẇẅẘẉⱳὼὠὡὢὣωὤὥὦὧῲῳῴῶῷⱲѡԝᴡώᾠᾡᾢᾣᾤᾥᾦɯ𝝕𝟉𝞏'],
['X', 'ꭓꭕ𝛘𝜒𝝌𝞆𝟀ⲭ🞨𝑿𝛸🄧🞩🞪🅇🞫🞬𐌗Ⲭꓫ𝖃𝞦𝘟𐊐𝚾𝝬𝜲Ꭓ𐌢𝖷𝑋𝕏𝔛𐊴𝗫🆇🅧❌Ⓧ𝓧XẊ᙭χㄨ𝒳ӾჯӼҳЖΧҲᵡˣ⒳אሸẌꊼⅩХ╳᙮ᕁᕽⅹᚷⵝ𝙓𝚇乂𝐗🇽'],
[
'X',
'ꭓꭕ𝛘𝜒𝝌𝞆𝟀ⲭ🞨𝑿𝛸🄧🞩🞪🅇🞫🞬𐌗Ⲭꓫ𝖃𝞦𝘟𐊐𝚾𝝬𝜲Ꭓ𐌢𝖷𝑋𝕏𝔛𐊴𝗫🆇🅧❌Ⓧ𝓧XẊ᙭χㄨ𝒳ӾჯӼҳЖΧҲᵡˣ⒳אሸẌꊼⅩХ╳᙮ᕁᕽⅹᚷⵝ𝙓𝚇乂𝐗🇽'
],
['x', '᙮ⅹ𝑥𝒙𝓍𝔵𝕩𝖝𝗑𝘅ᕁᕽⓧxхẋ×ₓ⤫⤬⨯ẍᶍ𝙭ӽ𝘹𝐱𝚡⨰メ𝔁'],
['Y', '𝒴🄨𝓨𝔜𝖄𝖸𝘠𝙔𝚼𝛶𝝪𝞤УᎩᎽⲨ𝚈𝑌𝗬𝐘ꓬ𝒀𝜰𐊲🆈🅨ⓎYὛƳㄚʏ⅄ϔ¥¥ՎϓγץӲЧЎሃŸɎϤΥϒҮỲÝŶỸȲẎỶỴῨῩῪΎὙὝὟΫΎӮӰҰұ𝕐🇾'],
[
'Y',
'𝒴🄨𝓨𝔜𝖄𝖸𝘠𝙔𝚼𝛶𝝪𝞤УᎩᎽⲨ𝚈𝑌𝗬𝐘ꓬ𝒀𝜰𐊲🆈🅨ⓎYὛƳㄚʏ⅄ϔ¥¥ՎϓγץӲЧЎሃŸɎϤΥϒҮỲÝŶỸȲẎỶỴῨῩῪΎὙὝὟΫΎӮӰҰұ𝕐🇾'
],
['y', '𝐲𝑦𝒚𝓎𝔂𝔶𝕪𝖞𝗒𝘆𝘺𝙮𝚢ʏỿꭚγℽ𝛄𝛾𝜸𝝲𝞬🅈ᎽᎩⓨyỳýŷỹȳẏÿỷуყẙỵƴɏᵞɣʸᶌү⒴ӳӱӯўУʎ'],
['Z', '🄩🅉ꓜ𝗭𝐙☡Ꮓ𝘡🆉🅩ⓏZẔƵ乙ẐȤᶻ⒵ŹℤΖŻŽẒⱫ🇿'],
['z', '𝑍𝒁𝒵𝓩𝖹𝙕𝚉𝚭𝛧𝜡𝝛𝞕ᵶꮓ𝐳𝑧𝒛𝓏𝔃𝔷𝕫𝖟𝗓𝘇𝘻𝙯𝚣ⓩzźẑżžẓẕƶȥɀᴢጊʐⱬᶎʑᙆ']
]);
/** @copyright Mathias Bynens <https://mathiasbynens.be/>. MIT license. */
const regexLineBreakCombiningMarks = /[\0-\x08\x0E-\x1F\x7F-\x84\x86-\x9F\u0300-\u034E\u0350-\u035B\u0363-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u200C\u200E\u200F\u202A-\u202E\u2066-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3035\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFFF9-\uFFFB]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDCA-\uDDCC\uDE2C-\uDE37\uDE3E\uDEDF-\uDEEA\uDF00-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC35-\uDC46\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDEAB-\uDEB7]|\uD807[\uDC2F-\uDC36\uDC38-\uDC3F\uDC92-\uDCA7\uDCA9-\uDCB6]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD82F[\uDC9D\uDC9E\uDCA0-\uDCA3]|\uD834[\uDD65-\uDD69\uDD6D-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD4A]|\uDB40[\uDC01\uDC20-\uDC7F\uDD00-\uDDEF]/g;
const regexLineBreakCombiningMarks =
/[\0-\x08\x0E-\x1F\x7F-\x84\x86-\x9F\u0300-\u034E\u0350-\u035B\u0363-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u200C\u200E\u200F\u202A-\u202E\u2066-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3035\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFFF9-\uFFFB]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDCA-\uDDCC\uDE2C-\uDE37\uDE3E\uDEDF-\uDEEA\uDF00-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC35-\uDC46\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDEAB-\uDEB7]|\uD807[\uDC2F-\uDC36\uDC38-\uDC3F\uDC92-\uDCA7\uDCA9-\uDCB6]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD82F[\uDC9D\uDC9E\uDCA0-\uDCA3]|\uD834[\uDD65-\uDD69\uDD6D-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD4A]|\uDB40[\uDC01\uDC20-\uDC7F\uDD00-\uDDEF]/g;
/** @copyright Mathias Bynens <https://mathiasbynens.be/>. MIT license. */
const regexSymbolWithCombiningMarks = /([\0-\u02FF\u0370-\u1AAF\u1B00-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uE000-\uFE1F\uFE30-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])([\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]+)/g;
const regexSymbolWithCombiningMarks =
/([\0-\u02FF\u0370-\u1AAF\u1B00-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uE000-\uFE1F\uFE30-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])([\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]+)/g;
const confusablesToChar = new Map();
for (const [char_, confusables] of charToConfusables) {
@@ -105,15 +167,24 @@ const removeConfusables = function (a) {
}
let ret = '';
for (const char_ of a.normalize().replace(regexLineBreakCombiningMarks, '').replace(regexSymbolWithCombiningMarks, '$1').replace(/\s/g, '')) {
for (const char_ of a
.normalize()
.replace(regexLineBreakCombiningMarks, '')
.replace(regexSymbolWithCombiningMarks, '$1')
.replace(/\s/g, '')) {
ret += confusablesToChar.get(char_) || char_;
}
return ret;
}
};
const removeWhitespace = function (a) {
return a.replace(/\s/g, '');
}
};
export { removeConfusables as default, confusablesToChar, charToConfusables, removeWhitespace };
export {
removeConfusables as default,
confusablesToChar,
charToConfusables,
removeWhitespace
};

View File

@@ -1,17 +1,17 @@
import sqliteService from './sqlite.js';
import { feed } from './database/feed.js';
import { gameLog } from './database/gameLog.js';
import { notifications } from './database/notifications.js';
import { moderation } from './database/moderation.js';
import { friendLogHistory } from './database/friendLogHistory.js';
import { friendLogCurrent } from './database/friendLogCurrent.js';
import { memos } from './database/memos.js';
import { avatarFavorites } from './database/avatarFavorites.js';
import { worldFavorites } from './database/worldFavorites.js';
import { feed } from './database/feed.js';
import { friendLogCurrent } from './database/friendLogCurrent.js';
import { friendLogHistory } from './database/friendLogHistory.js';
import { gameLog } from './database/gameLog.js';
import { memos } from './database/memos.js';
import { moderation } from './database/moderation.js';
import { notifications } from './database/notifications.js';
import { tableAlter } from './database/tableAlter.js';
import { tableFixes } from './database/tableFixes.js';
import { tableSize } from './database/tableSize.js';
import { worldFavorites } from './database/worldFavorites.js';
import sqliteService from './sqlite.js';
const dbVars = {
userId: '',

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const avatarFavorites = {
addAvatarToCache(entry) {
sqliteService.executeNonQuery(

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const feed = {
async getFeedDatabase() {
var feedDatabase = [];

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const friendLogCurrent = {
async getFriendLogCurrent() {
var friendLogCurrent = [];

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const friendLogHistory = {
async getFriendLogHistory() {
var friendLogHistory = [];

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const gameLog = {
async getGamelogDatabase() {
var gamelogDatabase = [];

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const memos = {
// user memos

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const moderation = {
async getModeration(userId) {
var row = {};

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const notifications = {
async getNotifications() {
var notifications = [];

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const tableAlter = {
async upgradeDatabaseVersion() {
// var version = 0;

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const tableFixes = {
async cleanLegendFromFriendLog() {
await sqliteService.executeNonQuery(

View File

@@ -1,6 +1,7 @@
import sqliteService from '../sqlite.js';
import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const tableSize = {
async getMaxFriendLogNumber() {
var friendNumber = 0;

View File

@@ -1,5 +1,5 @@
import * as workerTimers from 'worker-timers';
/* eslint-disable no-unused-vars */
let VRCXStorage = {};
export default class {

View File

@@ -1,8 +1,7 @@
import { ElMessage, ElMessageBox } from 'element-plus';
import Noty from 'noty';
import { ElMessageBox, ElMessage } from 'element-plus';
import { i18n } from '../plugin/i18n';
import { statusCodes } from '../shared/constants/api.js';
import { escapeTag } from '../shared/utils';
import {
useAuthStore,
useAvatarStore,
@@ -11,9 +10,13 @@ import {
useUserStore
} from '../stores';
import { AppDebug } from './appConfig.js';
import webApiService from './webapi.js';
import { escapeTag } from '../shared/utils';
import { i18n } from '../plugin/i18n';
import { statusCodes } from '../shared/constants/api.js';
import { watchState } from './watchState';
import webApiService from './webapi.js';
const pendingGetRequests = new Map();
export let failedGetRequests = new Map();

View File

@@ -1,7 +1,5 @@
import Noty from 'noty';
import * as workerTimers from 'worker-timers';
import { groupRequest } from '../api';
import { escapeTag, parseLocation } from '../shared/utils';
import {
useFriendStore,
useGalleryStore,
@@ -13,10 +11,14 @@ import {
useUiStore,
useUserStore
} from '../stores';
import { escapeTag, parseLocation } from '../shared/utils';
import { AppDebug } from './appConfig';
import { groupRequest } from '../api';
import { request } from './request';
import { watchState } from './watchState';
import * as workerTimers from 'worker-timers';
let webSocket = null;
let lastWebSocketMessage = '';

View File

@@ -1,9 +1,6 @@
import amoled from '../../assets/scss/themes/theme.amoled.scss?url';
import dark from '../../assets/scss/themes/theme.dark.scss?url';
import darkblue from '../../assets/scss/themes/theme.darkblue.scss?url';
import amoled from '../../assets/scss/themes/theme.amoled.scss?url';
// import darkvanillaold from '../../assets/scss/themes/theme.darkvanillaold.scss?url';
// import darkvanilla from '../../assets/scss/themes/theme.darkvanilla.scss?url';
// import pink from '../../assets/scss/themes/theme.pink.scss?url';
import material3 from '../../assets/scss/themes/theme.material3.scss?url';
export const THEME_CONFIG = {

View File

@@ -1,16 +1,16 @@
import {
compareByName,
compareByCreatedAt,
compareByCreatedAtAscending,
compareByUpdatedAt,
compareByDisplayName,
compareByMemberCount,
compareByPrivate,
compareByStatus,
compareByLastActive,
compareByLastSeen,
compareByLocation,
compareByLocationAt,
compareByLocation
compareByMemberCount,
compareByName,
compareByPrivate,
compareByStatus,
compareByUpdatedAt
} from '../compare';
describe('Compare Functions', () => {

View File

@@ -1,4 +1,4 @@
import { sortStatus, isFriendOnline } from '../friend';
import { isFriendOnline, sortStatus } from '../friend';
describe('Friend Utils', () => {
describe('sortStatus', () => {

View File

@@ -1,7 +1,7 @@
import {
getEmojiFileName,
getPrintFileName,
getPrintLocalDate,
getEmojiFileName
getPrintLocalDate
} from '../gallery';
describe('Gallery Utils', () => {

View File

@@ -1,4 +1,4 @@
import { parseLocation, displayLocation } from '../location';
import { displayLocation, parseLocation } from '../location';
describe('Location Utils', () => {
describe('parseLocation', () => {

View File

@@ -1,5 +1,5 @@
import { useAuthStore } from '../../stores';
import { replaceBioSymbols } from './common';
import { useAuthStore } from '../../stores';
/**
*

View File

@@ -1,10 +1,10 @@
import {
changeLogRemoveLinks,
commaNumber,
escapeTag,
escapeTagRecursive,
textToHex,
commaNumber,
localeIncludes,
changeLogRemoveLinks
textToHex
} from '../string';
describe('String Utils', () => {

View File

@@ -1,10 +1,10 @@
import { useAvatarStore, useWorldStore } from '../../../stores';
import { compareUnityVersion } from '../avatar';
import {
extractFileId,
extractFileVersion,
extractVariantVersion
} from '../common';
import { useAvatarStore, useWorldStore } from '../../../stores';
import { compareUnityVersion } from '../avatar';
/**
*

View File

@@ -1,7 +1,8 @@
import { ElMessage } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useAppearanceSettingsStore, useUiStore } from '../../../stores';
import { THEME_CONFIG } from '../../constants';
import { ElMessage } from 'element-plus';
import { i18n } from '../../../plugin/i18n';
/**

View File

@@ -1,15 +1,17 @@
import Noty from 'noty';
import { ElMessage, ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
import { ElMessageBox, ElMessage } from 'element-plus';
import { miscRequest } from '../../api';
import Noty from 'noty';
import {
useAvatarStore,
useInstanceStore,
useWorldStore,
useSearchStore
useSearchStore,
useWorldStore
} from '../../stores';
import { compareUnityVersion } from './avatar';
import { escapeTag } from './base/string';
import { miscRequest } from '../../api';
/**
*

View File

@@ -96,10 +96,7 @@ function compareByDisplayName(a, b) {
* @returns
*/
function compareById(a, b) {
if (
typeof a.id !== 'string' ||
typeof b.id !== 'string'
) {
if (typeof a.id !== 'string' || typeof b.id !== 'string') {
return 0;
}
return a.id.localeCompare(b.id);

View File

@@ -47,7 +47,10 @@ export function handleImageUploadInput(event, options = {}) {
const file = files[0];
if (file.size >= maxSize) {
if (tooLargeMessage) {
ElMessage({ message: resolveMessage(tooLargeMessage), type: 'error' });
ElMessage({
message: resolveMessage(tooLargeMessage),
type: 'error'
});
}
clearInput();
return { file: null, clearInput };
@@ -55,12 +58,18 @@ export function handleImageUploadInput(event, options = {}) {
let acceptRegex = null;
if (acceptPattern) {
acceptRegex = acceptPattern instanceof RegExp ? acceptPattern : new RegExp(acceptPattern);
acceptRegex =
acceptPattern instanceof RegExp
? acceptPattern
: new RegExp(acceptPattern);
}
if (acceptRegex && !acceptRegex.test(file.type)) {
if (invalidTypeMessage) {
ElMessage({ message: resolveMessage(invalidTypeMessage), type: 'error' });
ElMessage({
message: resolveMessage(invalidTypeMessage),
type: 'error'
});
}
clearInput();
return { file: null, clearInput };

View File

@@ -1,8 +1,8 @@
import { useAppearanceSettingsStore, useUserStore } from '../../stores';
import { languageMappings } from '../constants';
import { timeToText } from './base/format';
import { HueToHex } from './base/ui';
import { convertFileUrlToImageUrl } from './common';
import { languageMappings } from '../constants';
import { timeToText } from './base/format';
/**
*

View File

@@ -1,6 +1,7 @@
import { worldRequest } from '../../api';
import { parseLocation } from './location';
import { rpcWorlds } from '../constants';
import { worldRequest } from '../../api';
/**
*
* @param {string} location

View File

@@ -1,23 +1,26 @@
import Noty from 'noty';
import { reactive, ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { defineStore } from 'pinia';
import { ref, reactive, watch } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import { authRequest } from '../api';
import { useI18n } from 'vue-i18n';
import configRepository from '../service/config';
import { database } from '../service/database';
import { AppDebug } from '../service/appConfig';
import { request } from '../service/request';
import security from '../service/security';
import webApiService from '../service/webapi';
import Noty from 'noty';
import { closeWebSocket, initWebsocket } from '../service/websocket';
import { watchState } from '../service/watchState';
import { AppDebug } from '../service/appConfig';
import { authRequest } from '../api';
import { database } from '../service/database';
import { escapeTag } from '../shared/utils';
import { useNotificationStore } from './notification';
import { request } from '../service/request';
import { useAdvancedSettingsStore } from './settings/advanced';
import { useNotificationStore } from './notification';
import { useUpdateLoopStore } from './updateLoop';
import { useUserStore } from './user';
import { useVrcxStore } from './vrcx';
import { watchState } from '../service/watchState';
import configRepository from '../service/config';
import security from '../service/security';
import webApiService from '../service/webapi';
export const useAuthStore = defineStore('Auth', () => {
const advancedSettingsStore = useAdvancedSettingsStore();

View File

@@ -1,11 +1,7 @@
import { defineStore } from 'pinia';
import { ref, watch, nextTick } from 'vue';
import { nextTick, ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { avatarRequest, miscRequest } from '../api';
import { database } from '../service/database';
import { AppDebug } from '../service/appConfig';
import webApiService from '../service/webapi';
import { watchState } from '../service/watchState';
import { defineStore } from 'pinia';
import {
checkVRChatCache,
extractFileId,
@@ -15,11 +11,17 @@ import {
replaceBioSymbols,
storeAvatarImage
} from '../shared/utils';
import { avatarRequest, miscRequest } from '../api';
import { AppDebug } from '../service/appConfig';
import { database } from '../service/database';
import { useAdvancedSettingsStore } from './settings/advanced';
import { useAvatarProviderStore } from './avatarProvider';
import { useFavoriteStore } from './favorite';
import { useAdvancedSettingsStore } from './settings/advanced';
import { useUserStore } from './user';
import { useVRCXUpdaterStore } from './vrcxUpdater';
import { watchState } from '../service/watchState';
import webApiService from '../service/webapi';
export const useAvatarStore = defineStore('Avatar', () => {
const favoriteStore = useFavoriteStore();

Some files were not shown because too many files have changed in this diff Show More