refactor: app.js (#1291)

* refactor: frontend

* Fix avatar gallery sort

* Update .NET dependencies

* Update npm dependencies

electron v37.1.0

* bulkRefreshFriends

* fix dark theme

* Remove crowdin

* Fix config.json dialog not updating

* VRCX log file fixes & add Cef log

* Remove SharedVariable, fix startup

* Revert init theme change

* Logging date not working? Fix WinformThemer designer error

* Add Cef request hander, no more escaping main page

* clean

* fix

* fix

* clean

* uh

* Apply thememode at startup, fixes random user colours

* Split database into files

* Instance info remove empty lines

* Open external VRC links with VRCX

* Electron fixes

* fix userdialog style

* ohhhh

* fix store

* fix store

* fix: load all group members after kicking a user

* fix: world dialog favorite button style

* fix: Clear VRCX Cache Timer input value

* clean

* Fix VR overlay

* Fix VR overlay 2

* Fix Discord discord rich presence for RPC worlds

* Clean up age verified user tags

* Fix playerList being occupied after program reload

* no `this`

* Fix login stuck loading

* writable: false

* Hide dialogs on logout

* add flush sync option

* rm LOGIN event

* rm LOGOUT event

* remove duplicate event listeners

* remove duplicate event listeners

* clean

* remove duplicate event listeners

* clean

* fix theme style

* fix t

* clearable

* clean

* fix ipcEvent

* Small changes

* Popcorn Palace support

* Remove checkActiveFriends

* Clean up

* Fix dragEnterCef

* Block API requests when not logged in

* Clear state on login & logout

* Fix worldDialog instances not updating

* use <script setup>

* Fix avatar change event, CheckGameRunning at startup

* Fix image dragging

* fix

* Remove PWI

* fix updateLoop

* add webpack-dev-server to dev environment

* rm unnecessary chunks

* use <script setup>

* webpack-dev-server changes

* use <script setup>

* use <script setup>

* Fix UGC text size

* Split login event

* t

* use <script setup>

* fix

* Update .gitignore and enable checkJs in jsconfig

* fix i18n t

* use <script setup>

* use <script setup>

* clean

* global types

* fix

* use checkJs for debugging

* Add watchState for login watchers

* fix .vue template

* type fixes

* rm Vue.filter

* Cef v138.0.170, VC++ 2022

* Settings fixes

* Remove 'USER:CURRENT'

* clean up 2FA callbacks

* remove userApply

* rm i18n import

* notification handling to use notification store methods

* refactor favorite handling to use favorite store methods and clean up event emissions

* refactor moderation handling to use dedicated functions for player moderation events

* refactor friend handling to use dedicated functions for friend events

* Fix program startup, move lang init

* Fix friend state

* Fix status change error

* Fix user notes diff

* fix

* rm group event

* rm auth event

* rm avatar event

* clean

* clean

* getUser

* getFriends

* getFavoriteWorlds, getFavoriteAvatars

* AvatarGalleryUpload btn style & package.json update

* Fix friend requests

* Apply user

* Apply world

* Fix note diff

* Fix VR overlay

* Fixes

* Update build scripts

* Apply avatar

* Apply instance

* Apply group

* update hidden VRC+ badge

* Fix sameInstance "private"

* fix 502/504 API errors

* fix 502/504 API errors

* clean

* Fix friend in same instance on orange showing twice in friends list

* Add back in broken friend state repair methods

* add types

---------

Co-authored-by: Natsumi <cmcooper123@hotmail.com>
This commit is contained in:
pa
2025-07-14 12:00:08 +09:00
committed by GitHub
parent 952fd77ed5
commit f4f78bb5ec
323 changed files with 47745 additions and 43326 deletions

View File

@@ -1,125 +0,0 @@
import utils from '../../classes/utils';
function storeAvatarImage(args) {
const refCreatedAt = args.json.versions[0];
const fileCreatedAt = refCreatedAt.created_at;
const fileId = args.params.fileId;
let avatarName = '';
const imageName = args.json.name;
const avatarNameRegex = /Avatar - (.*) - Image -/gi.exec(imageName);
if (avatarNameRegex) {
avatarName = utils.replaceBioSymbols(avatarNameRegex[1]);
}
const ownerId = args.json.ownerId;
const avatarInfo = {
ownerId,
avatarName,
fileCreatedAt
};
window.API.cachedAvatarNames.set(fileId, avatarInfo);
return avatarInfo;
}
function parseAvatarUrl(avatar) {
const url = new URL(avatar);
const urlPath = url.pathname;
if (urlPath.substring(5, 13) === '/avatar/') {
const avatarId = urlPath.substring(13);
return avatarId;
}
// return void 0;
}
function getPlatformInfo(unityPackages) {
var pc = {};
var android = {};
var ios = {};
if (typeof unityPackages === 'object') {
for (var unityPackage of unityPackages) {
if (
unityPackage.variant &&
unityPackage.variant !== 'standard' &&
unityPackage.variant !== 'security'
) {
continue;
}
if (unityPackage.platform === 'standalonewindows') {
if (
unityPackage.performanceRating === 'None' &&
pc.performanceRating
) {
continue;
}
pc = unityPackage;
} else if (unityPackage.platform === 'android') {
if (
unityPackage.performanceRating === 'None' &&
android.performanceRating
) {
continue;
}
android = unityPackage;
} else if (unityPackage.platform === 'ios') {
if (
unityPackage.performanceRating === 'None' &&
ios.performanceRating
) {
continue;
}
ios = unityPackage;
}
}
}
return { pc, android, ios };
}
function compareUnityVersion(unitySortNumber) {
if (!window.API.cachedConfig.sdkUnityVersion) {
console.error('No cachedConfig.sdkUnityVersion');
return false;
}
// 2022.3.6f1 2022 03 06 000
// 2019.4.31f1 2019 04 31 000
// 5.3.4p1 5 03 04 010
// 2019.4.31f1c1 is a thing
var array = window.API.cachedConfig.sdkUnityVersion.split('.');
if (array.length < 3) {
console.error('Invalid cachedConfig.sdkUnityVersion');
return false;
}
var currentUnityVersion = array[0];
currentUnityVersion += array[1].padStart(2, '0');
var indexFirstLetter = array[2].search(/[a-zA-Z]/);
if (indexFirstLetter > -1) {
currentUnityVersion += array[2]
.substr(0, indexFirstLetter)
.padStart(2, '0');
currentUnityVersion += '0';
var letter = array[2].substr(indexFirstLetter, 1);
if (letter === 'p') {
currentUnityVersion += '1';
} else {
// f
currentUnityVersion += '0';
}
currentUnityVersion += '0';
} else {
// just in case
currentUnityVersion += '000';
}
// just in case
currentUnityVersion = currentUnityVersion.replace(/\D/g, '');
if (parseInt(unitySortNumber, 10) <= parseInt(currentUnityVersion, 10)) {
return true;
}
return false;
}
export {
storeAvatarImage,
parseAvatarUrl,
getPlatformInfo,
compareUnityVersion
};

View File

@@ -1,164 +0,0 @@
import { reactive, ref } from 'vue';
import configRepository from '../../service/config';
function useModerationTable() {
const groupInvitesModerationTable = reactive({
data: [],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 20, 25, 50, 100]
}
});
const groupJoinRequestsModerationTable = reactive({
data: [],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 20, 25, 50, 100]
}
});
const groupBlockedModerationTable = reactive({
data: [],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 20, 25, 50, 100]
}
});
const groupLogsModerationTable = reactive({
data: [],
filters: [{ prop: ['description'], value: '' }],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 20, 25, 50, 100]
}
});
const groupBansModerationTable = reactive({
data: [],
filters: [{ prop: ['$displayName'], value: '' }],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 20, 25, 50, 100]
}
});
const groupMemberModerationTable = reactive({
data: [],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 20, 25, 50, 100]
}
});
async function initializePageSize() {
try {
const tablePageSize = await configRepository.getInt(
'VRCX_tablePageSize',
15
);
groupMemberModerationTable.pageSize = tablePageSize;
groupBansModerationTable.pageSize = tablePageSize;
groupLogsModerationTable.pageSize = tablePageSize;
groupInvitesModerationTable.pageSize = tablePageSize;
groupJoinRequestsModerationTable.pageSize = tablePageSize;
groupBlockedModerationTable.pageSize = tablePageSize;
} catch (error) {
console.error('Failed to initialize table page size:', error);
}
}
function deselectGroupMember(userId) {
const deselectInTable = (tableData) => {
if (userId) {
const row = tableData.find((item) => item.userId === userId);
if (row) {
row.$selected = false;
}
} else {
tableData.forEach((row) => {
if (row.$selected) {
row.$selected = false;
}
});
}
};
deselectInTable(groupMemberModerationTable.data);
deselectInTable(groupBansModerationTable.data);
deselectInTable(groupInvitesModerationTable.data);
deselectInTable(groupJoinRequestsModerationTable.data);
deselectInTable(groupBlockedModerationTable.data);
}
return {
groupInvitesModerationTable,
groupJoinRequestsModerationTable,
groupBlockedModerationTable,
groupLogsModerationTable,
groupBansModerationTable,
groupMemberModerationTable,
initializePageSize,
deselectGroupMember
};
}
function useSelectedUsers() {
const selectedUsers = reactive({});
// computed not working here hmm
const selectedUsersArray = ref([]);
function groupMemberModerationTableSelectionChange(row) {
if (row.$selected && !selectedUsers[row.userId]) {
setSelectedUsers(row.userId, row);
} else if (!row.$selected && selectedUsers[row.userId]) {
deselectedUsers(row.userId);
}
}
function deselectedUsers(userId, isAll = false) {
if (isAll) {
for (const id in selectedUsers) {
if (Object.prototype.hasOwnProperty.call(selectedUsers, id)) {
delete selectedUsers[id];
}
}
} else {
if (Object.prototype.hasOwnProperty.call(selectedUsers, userId)) {
delete selectedUsers[userId];
}
}
selectedUsersArray.value = Object.values(selectedUsers);
}
function setSelectedUsers(usersId, user) {
if (!user) {
return;
}
selectedUsers[usersId] = user;
selectedUsersArray.value = Object.values(selectedUsers);
}
return {
selectedUsers,
selectedUsersArray,
groupMemberModerationTableSelectionChange,
deselectedUsers,
setSelectedUsers
};
}
export { useModerationTable, useSelectedUsers };

View File

@@ -1,14 +0,0 @@
function hasGroupPermission(ref, permission) {
if (
ref &&
ref.myMember &&
ref.myMember.permissions &&
(ref.myMember.permissions.includes('*') ||
ref.myMember.permissions.includes(permission))
) {
return true;
}
return false;
}
export { hasGroupPermission };

View File

@@ -1,168 +0,0 @@
import { instanceRequest } from '../../api';
// TODO: launch, invite, refresh, etc. buttons, better to split into one component
function refreshInstancePlayerCount(instance) {
const L = parseLocation(instance);
if (L.isRealInstance) {
instanceRequest.getInstance({
worldId: L.worldId,
instanceId: L.instanceId
});
}
}
function isRealInstance(instanceId) {
if (!instanceId) {
return false;
}
switch (instanceId) {
case ':':
case 'offline':
case 'offline:offline':
case 'private':
case 'private:private':
case 'traveling':
case 'traveling:traveling':
case instanceId.startsWith('local'):
return false;
}
return true;
}
function displayLocation(location, worldName, groupName) {
var text = worldName;
var L = parseLocation(location);
if (L.isOffline) {
text = 'Offline';
} else if (L.isPrivate) {
text = 'Private';
} else if (L.isTraveling) {
text = 'Traveling';
} else if (L.worldId) {
if (groupName) {
text = `${worldName} ${L.accessTypeName}(${groupName})`;
} else if (L.instanceId) {
text = `${worldName} ${L.accessTypeName}`;
}
}
return text;
}
function parseLocation(tag) {
var _tag = String(tag || '');
var ctx = {
tag: _tag,
isOffline: false,
isPrivate: false,
isTraveling: false,
isRealInstance: false,
worldId: '',
instanceId: '',
instanceName: '',
accessType: '',
accessTypeName: '',
region: '',
shortName: '',
userId: null,
hiddenId: null,
privateId: null,
friendsId: null,
groupId: null,
groupAccessType: null,
canRequestInvite: false,
strict: false,
ageGate: false
};
if (_tag === 'offline' || _tag === 'offline:offline') {
ctx.isOffline = true;
} else if (_tag === 'private' || _tag === 'private:private') {
ctx.isPrivate = true;
} else if (_tag === 'traveling' || _tag === 'traveling:traveling') {
ctx.isTraveling = true;
} else if (!_tag.startsWith('local')) {
ctx.isRealInstance = true;
var sep = _tag.indexOf(':');
// technically not part of instance id, but might be there when coping id from url so why not support it
var shortNameQualifier = '&shortName=';
var shortNameIndex = _tag.indexOf(shortNameQualifier);
if (shortNameIndex >= 0) {
ctx.shortName = _tag.substr(
shortNameIndex + shortNameQualifier.length
);
_tag = _tag.substr(0, shortNameIndex);
}
if (sep >= 0) {
ctx.worldId = _tag.substr(0, sep);
ctx.instanceId = _tag.substr(sep + 1);
ctx.instanceId.split('~').forEach((s, i) => {
if (i) {
var A = s.indexOf('(');
var Z = A >= 0 ? s.lastIndexOf(')') : -1;
var key = Z >= 0 ? s.substr(0, A) : s;
var value = A < Z ? s.substr(A + 1, Z - A - 1) : '';
if (key === 'hidden') {
ctx.hiddenId = value;
} else if (key === 'private') {
ctx.privateId = value;
} else if (key === 'friends') {
ctx.friendsId = value;
} else if (key === 'canRequestInvite') {
ctx.canRequestInvite = true;
} else if (key === 'region') {
ctx.region = value;
} else if (key === 'group') {
ctx.groupId = value;
} else if (key === 'groupAccessType') {
ctx.groupAccessType = value;
} else if (key === 'strict') {
ctx.strict = true;
} else if (key === 'ageGate') {
ctx.ageGate = true;
}
} else {
ctx.instanceName = s;
}
});
ctx.accessType = 'public';
if (ctx.privateId !== null) {
if (ctx.canRequestInvite) {
// InvitePlus
ctx.accessType = 'invite+';
} else {
// InviteOnly
ctx.accessType = 'invite';
}
ctx.userId = ctx.privateId;
} else if (ctx.friendsId !== null) {
// FriendsOnly
ctx.accessType = 'friends';
ctx.userId = ctx.friendsId;
} else if (ctx.hiddenId !== null) {
// FriendsOfGuests
ctx.accessType = 'friends+';
ctx.userId = ctx.hiddenId;
} else if (ctx.groupId !== null) {
// Group
ctx.accessType = 'group';
}
ctx.accessTypeName = ctx.accessType;
if (ctx.groupAccessType !== null) {
if (ctx.groupAccessType === 'public') {
ctx.accessTypeName = 'groupPublic';
} else if (ctx.groupAccessType === 'plus') {
ctx.accessTypeName = 'groupPlus';
}
}
} else {
ctx.worldId = _tag;
}
}
return ctx;
}
export {
refreshInstancePlayerCount,
isRealInstance,
displayLocation,
parseLocation
};

View File

@@ -1,236 +0,0 @@
const getOptions = (optionTypes) => {
const optionMap = {
Off: { label: 'Off', textKey: 'dialog.shared_feed_filters.off' },
On: { label: 'On', textKey: 'dialog.shared_feed_filters.on' },
VIP: {
label: 'VIP',
textKey: 'dialog.shared_feed_filters.favorite'
},
Friends: {
label: 'Friends',
textKey: 'dialog.shared_feed_filters.friends'
},
Everyone: {
label: 'Everyone',
textKey: 'dialog.shared_feed_filters.everyone'
}
};
return optionTypes.map((type) => optionMap[type]);
};
function feedFiltersOptions() {
const baseOptions = [
{
key: 'OnPlayerJoining',
name: 'OnPlayerJoining',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'OnPlayerJoined',
name: 'OnPlayerJoined',
options: getOptions(['Off', 'VIP', 'Friends', 'Everyone'])
},
{
key: 'OnPlayerLeft',
name: 'OnPlayerLeft',
options: getOptions(['Off', 'VIP', 'Friends', 'Everyone'])
},
{
key: 'Online',
name: 'Online',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'Offline',
name: 'Offline',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'GPS',
name: 'GPS',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'Status',
name: 'Status',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'invite',
name: 'Invite',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'requestInvite',
name: 'Request Invite',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'inviteResponse',
name: 'Invite Response',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'requestInviteResponse',
name: 'Request Invite Response',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'friendRequest',
name: 'Friend Request',
options: getOptions(['Off', 'On'])
},
{
key: 'Friend',
name: 'New Friend',
options: getOptions(['Off', 'On'])
},
{
key: 'Unfriend',
name: 'Unfriend',
options: getOptions(['Off', 'On'])
},
{
key: 'DisplayName',
name: 'Display Name Change',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'TrustLevel',
name: 'Trust Level Change',
options: getOptions(['Off', 'VIP', 'Friends'])
},
{
key: 'groupChange',
name: 'Group Change',
options: getOptions(['Off', 'On']),
tooltip:
"When you've left or been kicked from a group, group name changed, group owner changed, role added/removed"
},
{
key: 'group.announcement',
name: 'Group Announcement',
options: getOptions(['Off', 'On'])
},
{
key: 'group.informative',
name: 'Group Join',
options: getOptions(['Off', 'On']),
tooltip: 'When your request to join a group has been approved'
},
{
key: 'group.invite',
name: 'Group Invite',
options: getOptions(['Off', 'On']),
tooltip: 'When someone invites you to join a group'
},
{
key: 'group.joinRequest',
name: 'Group Join Request',
options: getOptions(['Off', 'On']),
tooltip:
"When someone requests to join a group you're a moderator for"
},
{
key: 'group.transfer',
name: 'Group Transfer Request',
options: getOptions(['Off', 'On'])
},
{
key: 'group.queueReady',
name: 'Instance Queue Ready',
options: getOptions(['Off', 'On'])
},
{
key: 'instance.closed',
name: 'Instance Closed',
options: getOptions(['Off', 'On']),
tooltip:
"When the instance you're in has been closed preventing anyone from joining"
},
{
key: 'VideoPlay',
name: 'Video Play',
options: getOptions(['Off', 'On']),
tooltip: 'Requires VRCX YouTube API option enabled',
tooltipIcon: 'el-icon-warning'
},
{
key: 'Event',
name: 'Miscellaneous Events',
options: getOptions(['Off', 'On']),
tooltip:
'Misc event from VRC game log: VRC crash auto rejoin, shader keyword limit, joining instance blocked by master, error loading video, audio device changed, error joining instance, kicked from instance, VRChat failing to start OSC server, etc...'
},
{
key: 'External',
name: 'External App',
options: getOptions(['Off', 'On'])
},
{
key: 'BlockedOnPlayerJoined',
name: 'Blocked Player Joins',
options: getOptions(['Off', 'VIP', 'Friends', 'Everyone'])
},
{
key: 'BlockedOnPlayerLeft',
name: 'Blocked Player Leaves',
options: getOptions(['Off', 'VIP', 'Friends', 'Everyone'])
},
{
key: 'MutedOnPlayerJoined',
name: 'Muted Player Joins',
options: getOptions(['Off', 'VIP', 'Friends', 'Everyone'])
},
{
key: 'MutedOnPlayerLeft',
name: 'Muted Player Leaves',
options: getOptions(['Off', 'VIP', 'Friends', 'Everyone'])
},
{
key: 'AvatarChange',
name: 'Lobby Avatar Change',
options: getOptions(['Off', 'VIP', 'Friends', 'Everyone'])
}
];
const photonFeedFiltersOptions = [
{
key: 'PortalSpawn',
name: 'Portal Spawn',
options: getOptions(['Off', 'VIP', 'Friends', 'Everyone'])
},
{
key: 'ChatBoxMessage',
name: 'Lobby ChatBox Message',
options: getOptions(['Off', 'VIP', 'Friends', 'Everyone'])
},
{ key: 'Blocked', name: 'Blocked', options: getOptions(['Off', 'On']) },
{
key: 'Unblocked',
name: 'Unblocked',
options: getOptions(['Off', 'On'])
},
{ key: 'Muted', name: 'Muted', options: getOptions(['Off', 'On']) },
{ key: 'Unmuted', name: 'Unmuted', options: getOptions(['Off', 'On']) }
];
const notyFeedFiltersOptions = baseOptions;
const wristFeedFiltersOptions = [
{
key: 'Location',
name: 'Self Location',
options: getOptions(['Off', 'On'])
},
...baseOptions
];
return {
notyFeedFiltersOptions,
wristFeedFiltersOptions,
photonFeedFiltersOptions
};
}
export { feedFiltersOptions };

View File

@@ -1,424 +0,0 @@
const openSourceSoftwareLicenses = [
{
name: 'animate.css',
licenseText: `The MIT License (MIT)
Copyright (c) 2019 Daniel Eden
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.`
},
{
name: 'CefSharp',
licenseText: `// Copyright © The CefSharp Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// * Neither the name of Google Inc. nor the name Chromium Embedded
// Framework nor the name CefSharp nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`
},
{
name: 'DiscordRichPresence',
licenseText: `MIT License
Copyright (c) 2018 Lachee
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.`
},
{
name: 'element',
licenseText: `The MIT License (MIT)
Copyright (c) 2016-present ElemeFE
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.`
},
{
name: 'librsync.net',
licenseText: `The MIT License (MIT)
Copyright (c) 2015 Brad Dodson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.`
},
{
name: 'Newtonsoft.Json',
licenseText: `The MIT License (MIT)
Copyright (c) 2007 James Newton-King
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`
},
{
name: 'normalize',
licenseText: `The MIT License (MIT)
Copyright © Nicolas Gallagher and Jonathan Neal
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`
},
{
name: 'noty',
licenseText: `Copyright (c) 2012 Nedim Arabacı
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`
},
{
name: 'OpenVR SDK',
licenseText: `Copyright (c) 2015, Valve Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`
},
{
name: 'Twemoji',
licenseText: `MIT License
Copyright (c) 2021 Twitter
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.`
},
{
name: 'SharpDX',
licenseText: `Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.`
},
{
name: 'vue',
licenseText: `The MIT License (MIT)
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.`
},
{
name: 'vue-data-tables',
licenseText: `The MIT License (MIT)
Copyright (c) 2018 Leon Zhang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.`
},
{
name: 'vue-lazyload',
licenseText: `The MIT License (MIT)
Copyright (c) 2016 Awe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.`
},
{
name: 'Encode Sans Font (from Dark Vanilla)',
licenseText: `SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
Copyright (c) 2020 June 20, Impallari Type, Andres Torresi, Jacques Le Bailly
(https://fonts.google.com/specimen/Encode+Sans),
with Reserved Font Name: Encode Sans.
PREAMBLE:
The goals of the Open Font License (OFL) are to stimulate worldwide development
of collaborative font projects, to support the font creation efforts of academic
and linguistic communities, and to provide a free and open framework in which
fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and redistributed
freely as long as they are not sold by themselves. The fonts, including any
derivative works, can be bundled, embedded, redistributed and/or sold with any
software provided that any reserved names are not used by derivative works.
The fonts and derivatives, however, cannot be released under any other type of
license. The requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of
the Font Software, to use, study, copy, merge, embed, modify, redistribute, and
sell modified and unmodified copies of the Font Software, subject to the
following conditions:
1. Neither the Font Software nor any of its individual components, in Original or
Modified Versions, may be sold by itself.
2. Original or Modified Versions of the Font Software may be bundled, redistributed
and/or sold with any software, provided that each copy contains the above copyright
notice and this license. These can be included either as stand-alone text files,
human-readable headers or in the appropriate machine-readable metadata fields within
text or binary files as long as those fields can be easily viewed by the user.
3. No Modified Version of the Font Software may use the Reserved Font Name(s) unless
explicit written permission is granted by the corresponding Copyright Holder. This
restriction only applies to the primary font name as presented to the users.
4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall
not be used to promote, endorse or advertise any Modified Version, except to
acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with
their explicit written permission.
5. The Font Software, modified or unmodified, in part or in whole, must be distributed
entirely under this license, and must not be distributed under any other license.
The requirement for fonts to remain under this license does not apply to any document
created using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR
OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
DEALINGS IN THE FONT SOFTWARE.`
},
{
name: 'Apache ECharts',
licenseText: `Apache License 2.0
Copyright 2017-2025 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (https://www.apache.org/).`
},
{
name: 'dayjs',
licenseText: `MIT License
Copyright (c) 2018-present, iamkun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.`
}
];
export { openSourceSoftwareLicenses };

View File

@@ -1,16 +0,0 @@
const VRChatScreenshotResolutions = [
{ name: '1280x720 (720p)', width: 1280, height: 720 },
{ name: '1920x1080 (1080p Default)', width: '', height: '' },
{ name: '2560x1440 (1440p)', width: 2560, height: 1440 },
{ name: '3840x2160 (4K)', width: 3840, height: 2160 }
];
const VRChatCameraResolutions = [
{ name: '1280x720 (720p)', width: 1280, height: 720 },
{ name: '1920x1080 (1080p Default)', width: '', height: '' },
{ name: '2560x1440 (1440p)', width: 2560, height: 1440 },
{ name: '3840x2160 (4K)', width: 3840, height: 2160 },
{ name: '7680x4320 (8K)', width: 7680, height: 4320 }
];
export { VRChatScreenshotResolutions, VRChatCameraResolutions };

View File

@@ -1,17 +0,0 @@
function getVRChatResolution(res) {
switch (res) {
case '1280x720':
return '1280x720 (720p)';
case '1920x1080':
return '1920x1080 (1080p)';
case '2560x1440':
return '2560x1440 (2K)';
case '3840x2160':
return '3840x2160 (4K)';
case '7680x4320':
return '7680x4320 (8K)';
}
return `${res} (Custom)`;
}
export { getVRChatResolution };

View File

@@ -1,106 +0,0 @@
const photonEmojis = [
'Angry',
'Blushing',
'Crying',
'Frown',
'Hand Wave',
'Hang Ten',
'In Love',
'Jack O Lantern',
'Kiss',
'Laugh',
'Skull',
'Smile',
'Spooky Ghost',
'Stoic',
'Sunglasses',
'Thinking',
'Thumbs Down',
'Thumbs Up',
'Tongue Out',
'Wow',
'Arrow Point',
"Can't see",
'Hourglass',
'Keyboard',
'No Headphones',
'No Mic',
'Portal',
'Shush',
'Bats',
'Cloud',
'Fire',
'Snow Fall',
'Snowball',
'Splash',
'Web',
'Beer',
'Candy',
'Candy Cane',
'Candy Corn',
'Champagne',
'Drink',
'Gingerbread',
'Ice Cream',
'Pineapple',
'Pizza',
'Tomato',
'Beachball',
'Coal',
'Confetti',
'Gift',
'Gifts',
'Life Ring',
'Mistletoe',
'Money',
'Neon Shades',
'Sun Lotion',
'Boo',
'Broken Heart',
'Exclamation',
'Go',
'Heart',
'Music Note',
'Question',
'Stop',
'Zzz'
];
const photonEventType = [
'MeshVisibility',
'AnimationFloat',
'AnimationBool',
'AnimationTrigger',
'AudioTrigger',
'PlayAnimation',
'SendMessage',
'SetParticlePlaying',
'TeleportPlayer',
'RunConsoleCommand',
'SetGameObjectActive',
'SetWebPanelURI',
'SetWebPanelVolume',
'SpawnObject',
'SendRPC',
'ActivateCustomTrigger',
'DestroyObject',
'SetLayer',
'SetMaterial',
'AddHealth',
'AddDamage',
'SetComponentActive',
'AnimationInt',
'AnimationIntAdd',
'AnimationIntSubtract',
'AnimationIntMultiply',
'AnimationIntDivide',
'AddVelocity',
'SetVelocity',
'AddAngularVelocity',
'SetAngularVelocity',
'AddForce',
'SetUIText',
'CallUdonMethod'
];
export { photonEmojis, photonEventType };

View File

@@ -1,341 +0,0 @@
import Noty from 'noty';
import utils from '../../classes/utils';
import { compareUnityVersion } from '../avatar/utils';
import {
displayLocation,
isRealInstance,
parseLocation
} from '../instance/utils';
import {
getPrintFileName,
getPrintLocalDate,
isFriendOnline
} from '../user/utils';
function getAvailablePlatforms(unityPackages) {
var isPC = false;
var isQuest = false;
var isIos = false;
if (typeof unityPackages === 'object') {
for (var unityPackage of unityPackages) {
if (
unityPackage.variant &&
unityPackage.variant !== 'standard' &&
unityPackage.variant !== 'security'
) {
continue;
}
if (unityPackage.platform === 'standalonewindows') {
isPC = true;
} else if (unityPackage.platform === 'android') {
isQuest = true;
} else if (unityPackage.platform === 'ios') {
isIos = true;
}
}
}
return { isPC, isQuest, isIos };
}
function downloadAndSaveJson(fileName, data) {
if (!fileName || !data) {
return;
}
try {
var link = document.createElement('a');
link.setAttribute(
'href',
`data:application/json;charset=utf-8,${encodeURIComponent(
JSON.stringify(data, null, 2)
)}`
);
link.setAttribute('download', `${fileName}.json`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch {
new Noty({
type: 'error',
text: utils.escapeTag('Failed to download JSON.')
}).show();
}
}
async function deleteVRChatCache(ref) {
var assetUrl = '';
var variant = '';
for (var i = ref.unityPackages.length - 1; i > -1; i--) {
var unityPackage = ref.unityPackages[i];
if (
unityPackage.variant &&
unityPackage.variant !== 'standard' &&
unityPackage.variant !== 'security'
) {
continue;
}
if (
unityPackage.platform === 'standalonewindows' &&
compareUnityVersion(unityPackage.unitySortNumber)
) {
assetUrl = unityPackage.assetUrl;
if (unityPackage.variant !== 'standard') {
variant = unityPackage.variant;
}
break;
}
}
var id = extractFileId(assetUrl);
var version = parseInt(extractFileVersion(assetUrl), 10);
var variantVersion = parseInt(extractVariantVersion(assetUrl), 10);
await AssetBundleManager.DeleteCache(id, version, variant, variantVersion);
}
async function checkVRChatCache(ref) {
if (!ref.unityPackages) {
return { Item1: -1, Item2: false, Item3: '' };
}
var assetUrl = '';
var variant = '';
for (var i = ref.unityPackages.length - 1; i > -1; i--) {
var unityPackage = ref.unityPackages[i];
if (unityPackage.variant && unityPackage.variant !== 'security') {
continue;
}
if (
unityPackage.platform === 'standalonewindows' &&
compareUnityVersion(unityPackage.unitySortNumber)
) {
assetUrl = unityPackage.assetUrl;
if (unityPackage.variant !== 'standard') {
variant = unityPackage.variant;
}
break;
}
}
var id = extractFileId(assetUrl);
var version = parseInt(extractFileVersion(assetUrl), 10);
var variantVersion = parseInt(extractVariantVersion(assetUrl), 10);
if (!id || !version) {
return { Item1: -1, Item2: false, Item3: '' };
}
return AssetBundleManager.CheckVRChatCache(
id,
version,
variant,
variantVersion
);
}
function copyToClipboard(text, message = 'Copied successfully!') {
navigator.clipboard
.writeText(text)
.then(() => {
window.$app.$message({
message: message,
type: 'success'
});
})
.catch((err) => {
console.error('Copy failed:', err);
window.$app.$message.error('Copy failed!');
});
}
function getFaviconUrl(resource) {
if (!resource) {
return '';
}
try {
const url = new URL(resource);
return `https://icons.duckduckgo.com/ip2/${url.host}.ico`;
} catch (err) {
console.error('Invalid URL:', err);
return '';
}
}
function convertFileUrlToImageUrl(url, resolution = 128) {
if (!url) {
return '';
}
/**
* possible patterns?
* /file/file_fileId/version
* /file/file_fileId/version/
* /file/file_fileId/version/file
* /file/file_fileId/version/file/
*/
const pattern = /file\/file_([a-f0-9-]+)\/(\d+)(\/file)?\/?$/;
const match = url.match(pattern);
if (match) {
const fileId = match[1];
const version = match[2];
return `https://api.vrchat.cloud/api/1/image/file_${fileId}/${version}/${resolution}`;
}
// no match return origin url
return url;
}
function replaceVrcPackageUrl(url) {
if (!url) {
return '';
}
return url.replace('https://api.vrchat.cloud/', 'https://vrchat.com/');
}
function getLaunchURL(instance) {
var L = instance;
if (L.instanceId) {
if (L.shortName) {
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
L.worldId
)}&instanceId=${encodeURIComponent(
L.instanceId
)}&shortName=${encodeURIComponent(L.shortName)}`;
}
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
L.worldId
)}&instanceId=${encodeURIComponent(L.instanceId)}`;
}
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
L.worldId
)}`;
}
function extractFileId(s) {
var match = String(s).match(/file_[0-9A-Za-z-]+/);
return match ? match[0] : '';
}
function extractFileVersion(s) {
var match = /(?:\/file_[0-9A-Za-z-]+\/)([0-9]+)/gi.exec(s);
return match ? match[1] : '';
}
function extractVariantVersion(url) {
if (!url) {
return '0';
}
try {
const params = new URLSearchParams(new URL(url).search);
const version = params.get('v');
if (version) {
return version;
}
return '0';
} catch {
return '0';
}
}
// ---------------------- devtool method --------------------------
async function getBundleLocation(input) {
const $app = window.$app;
var assetUrl = input;
var variant = '';
if (assetUrl) {
// continue
} else if (
$app.avatarDialog.visible &&
$app.avatarDialog.ref.unityPackages.length > 0
) {
var unityPackages = $app.avatarDialog.ref.unityPackages;
for (let i = unityPackages.length - 1; i > -1; i--) {
var unityPackage = unityPackages[i];
if (
unityPackage.variant &&
unityPackage.variant !== 'standard' &&
unityPackage.variant !== 'security'
) {
continue;
}
if (
unityPackage.platform === 'standalonewindows' &&
compareUnityVersion(unityPackage.unitySortNumber)
) {
assetUrl = unityPackage.assetUrl;
if (unityPackage.variant !== 'standard') {
variant = unityPackage.variant;
}
break;
}
}
} else if (
$app.worldDialog.visible &&
$app.worldDialog.ref.unityPackages.length > 0
) {
var unityPackages = $app.worldDialog.ref.unityPackages;
for (let i = unityPackages.length - 1; i > -1; i--) {
var unityPackage = unityPackages[i];
if (
unityPackage.platform === 'standalonewindows' &&
compareUnityVersion(unityPackage.unitySortNumber)
) {
assetUrl = unityPackage.assetUrl;
break;
}
}
}
if (!assetUrl) {
return null;
}
var fileId = extractFileId(assetUrl);
var fileVersion = parseInt(extractFileVersion(assetUrl), 10);
var variantVersion = parseInt(extractVariantVersion(assetUrl), 10);
var assetLocation = await AssetBundleManager.GetVRChatCacheFullLocation(
fileId,
fileVersion,
variant,
variantVersion
);
var cacheInfo = await AssetBundleManager.CheckVRChatCache(
fileId,
fileVersion,
variant,
variantVersion
);
var inCache = false;
if (cacheInfo.Item1 > 0) {
inCache = true;
}
console.log(`InCache: ${inCache}`);
var fullAssetLocation = `${assetLocation}\\__data`;
console.log(fullAssetLocation);
return fullAssetLocation;
}
const _utils = {
getAvailablePlatforms,
deleteVRChatCache,
checkVRChatCache,
getLaunchURL,
extractFileId,
extractFileVersion,
extractVariantVersion,
isRealInstance,
displayLocation,
parseLocation,
getPrintFileName,
getPrintLocalDate,
isFriendOnline
};
export {
getAvailablePlatforms,
downloadAndSaveJson,
deleteVRChatCache,
checkVRChatCache,
copyToClipboard,
getFaviconUrl,
convertFileUrlToImageUrl,
replaceVrcPackageUrl,
getLaunchURL,
extractFileId,
extractFileVersion,
extractVariantVersion,
getBundleLocation,
_utils
};

View File

@@ -1,34 +0,0 @@
const emojiAnimationStyleUrl =
'https://assets.vrchat.com/www/images/emoji-previews/';
const emojiAnimationStyleList = {
Aura: 'Preview_B2-Aura.gif',
Bats: 'Preview_B2-Fall_Bats.gif',
Bees: 'Preview_B2-Bees.gif',
Bounce: 'Preview_B2-Bounce.gif',
Cloud: 'Preview_B2-Cloud.gif',
Confetti: 'Preview_B2-Winter_Confetti.gif',
Crying: 'Preview_B2-Crying.gif',
Dislike: 'Preview_B2-Dislike.gif',
Fire: 'Preview_B2-Fire.gif',
Idea: 'Preview_B2-Idea.gif',
Lasers: 'Preview_B2-Lasers.gif',
Like: 'Preview_B2-Like.gif',
Magnet: 'Preview_B2-Magnet.gif',
Mistletoe: 'Preview_B2-Winter_Mistletoe.gif',
Money: 'Preview_B2-Money.gif',
Noise: 'Preview_B2-Noise.gif',
Orbit: 'Preview_B2-Orbit.gif',
Pizza: 'Preview_B2-Pizza.gif',
Rain: 'Preview_B2-Rain.gif',
Rotate: 'Preview_B2-Rotate.gif',
Shake: 'Preview_B2-Shake.gif',
Snow: 'Preview_B2-Spin.gif',
Snowball: 'Preview_B2-Winter_Snowball.gif',
Spin: 'Preview_B2-Spin.gif',
Splash: 'Preview_B2-SummerSplash.gif',
Stop: 'Preview_B2-Stop.gif',
ZZZ: 'Preview_B2-ZZZ.gif'
};
export { emojiAnimationStyleUrl, emojiAnimationStyleList };

View File

@@ -1,73 +0,0 @@
// vrchat to famfamfam language mappings
const languageMappings = {
eng: 'us',
kor: 'kr',
rus: 'ru',
spa: 'es',
por: 'pt',
zho: 'cn',
deu: 'de',
jpn: 'jp',
fra: 'fr',
swe: 'se',
nld: 'nl',
pol: 'pl',
dan: 'dk',
nor: 'no',
ita: 'it',
tha: 'th',
fin: 'fi',
hun: 'hu',
ces: 'cz',
tur: 'tr',
ara: 'ae',
ron: 'ro',
vie: 'vn',
ukr: 'ua',
ase: 'us',
bfi: 'gb',
dse: 'nl',
fsl: 'fr',
jsl: 'jp',
kvk: 'kr',
mlt: 'mt',
ind: 'id',
hrv: 'hr',
heb: 'he',
afr: 'af',
ben: 'be',
bul: 'bg',
cmn: 'cn',
cym: 'cy',
ell: 'el',
est: 'et',
fil: 'ph',
gla: 'gd',
gle: 'ga',
hin: 'hi',
hmn: 'cn',
hye: 'hy',
isl: 'is',
lav: 'lv',
lit: 'lt',
ltz: 'lb',
mar: 'hi',
mkd: 'mk',
msa: 'my',
sco: 'gd',
slk: 'sk',
slv: 'sl',
tel: 'hi',
mri: 'nz',
wuu: 'cn',
yue: 'cn',
tws: 'cn',
asf: 'au',
nzs: 'nz',
gsg: 'de',
epo: 'eo',
tok: 'tok'
};
export { languageMappings };

View File

@@ -1,16 +0,0 @@
const userDialogGroupSortingOptions = {
alphabetical: {
name: 'dialog.user.groups.sorting.alphabetical',
value: 'alphabetical'
},
members: {
name: 'dialog.user.groups.sorting.members',
value: 'members'
},
inGame: {
name: 'dialog.user.groups.sorting.in_game',
value: 'inGame'
}
};
export { userDialogGroupSortingOptions };

View File

@@ -1,89 +0,0 @@
import { languageMappings } from './constants/language';
function userOnlineForTimestamp(ctx) {
if (ctx.ref.state === 'online' && ctx.ref.$online_for) {
return ctx.ref.$online_for;
} else if (ctx.ref.state === 'active' && ctx.ref.$active_for) {
return ctx.ref.$active_for;
} else if (ctx.ref.$offline_for) {
return ctx.ref.$offline_for;
}
return 0;
}
function languageClass(language) {
const style = {};
const mapping = languageMappings[language];
if (typeof mapping !== 'undefined') {
style[mapping] = true;
} else {
style.unknown = true;
}
return style;
}
function getPrintFileName(print) {
const authorName = print.authorName;
// fileDate format: 2024-11-03_16-14-25.757
const createdAt = getPrintLocalDate(print);
const fileNameDate = createdAt
.toISOString()
.replace(/:/g, '-')
.replace(/T/g, '_')
.replace(/Z/g, '');
const fileName = `${authorName}_${fileNameDate}_${print.id}.png`;
return fileName;
}
function getEmojiFileName(emoji) {
if (emoji.frames) {
const loopStyle = emoji.loopStyle || 'linear';
return `${emoji.name}_${emoji.animationStyle}animationStyle_${emoji.frames}frames_${emoji.framesOverTime}fps_${loopStyle}loopStyle.png`;
} else {
return `${emoji.name}_${emoji.animationStyle}animationStyle.png`;
}
}
function getPrintLocalDate(print) {
if (print.createdAt) {
const createdAt = new Date(print.createdAt);
// cursed convert to local time
createdAt.setMinutes(
createdAt.getMinutes() - createdAt.getTimezoneOffset()
);
return createdAt;
}
if (print.timestamp) {
return new Date(print.timestamp);
}
const createdAt = new Date();
// cursed convert to local time
createdAt.setMinutes(
createdAt.getMinutes() - createdAt.getTimezoneOffset()
);
return createdAt;
}
function isFriendOnline(friend) {
if (typeof friend === 'undefined' || typeof friend.ref === 'undefined') {
return false;
}
if (friend.state === 'online') {
return true;
}
if (friend.state !== 'online' && friend.ref.location !== 'private') {
// wat
return true;
}
return false;
}
export {
userOnlineForTimestamp,
languageClass,
getPrintFileName,
getPrintLocalDate,
getEmojiFileName,
isFriendOnline
};