Photon logging support

This commit is contained in:
Natsumi
2021-11-20 23:53:37 +13:00
parent 1c79103c67
commit 3a102a2c2c
14 changed files with 2367 additions and 164 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -82,10 +82,14 @@
margin-top: 15px;
}
.el-table__expanded-cell[class*='cell'] {
.el-table--mini .el-table__expanded-cell[class*='cell'] {
padding: 20px 50px;
}
.el-table--mini .el-table__cell {
padding: 5px 0;
}
.el-dialog__body {
padding: 20px;
}
@@ -615,3 +619,20 @@ i.x-user-status.busy {
.el-form-item {
margin-bottom: 4px;
}
.photon-event-table .el-table--mini .el-table__cell,
.current-instance-table .el-table--mini .el-table__cell {
padding: 0;
}
.photon-event-table {
margin-top: 20px;
}
.current-instance-table img.friends-list-avatar {
width: unset;
height: 16px;
margin-right: 0;
margin-left: 3px;
border-radius: 2px;
}

View File

@@ -1 +1 @@
export var appVersion = 'VRCX 2021.11.04';
export var appVersion = 'VRCX 2021.11.20';

View File

@@ -59,6 +59,7 @@ html
span= name
+menuitem('feed', 'Feed', 'el-icon-news')
+menuitem('gameLog', 'Game Log', 'el-icon-s-data')
+menuitem('playerList', 'Player List', 'el-icon-tickets')
+menuitem('search', 'Search', 'el-icon-search')
+menuitem('favorite', 'Favorite', 'el-icon-star-off')
+menuitem('friendLog', 'Friend Log', 'el-icon-notebook-2')
@@ -68,6 +69,98 @@ html
+menuitem('profile', 'Profile', 'el-icon-user')
+menuitem('settings', 'Settings', 'el-icon-s-tools')
//- playerList
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'playerList'")
div(style="display:flex;flex-direction:column;height:100%")
div(v-if="currentInstanceWorld.id" style="display:flex")
el-popover(placement="right" width="500px" trigger="click" style="height:120px")
img.x-link(slot="reference" v-lazy="currentInstanceWorld.thumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px")
img.x-link(v-lazy="currentInstanceWorld.imageUrl" style="width:500px;height:375px" @click="openExternalLink(currentInstanceWorld.imageUrl)")
div(style="margin-left:10px;display:flex;flex-direction:column")
div
span.x-link(@click="showWorldDialog(currentInstanceWorld.id)" style="font-weight:bold;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1")
| #[i.el-icon-s-home(v-show="API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === currentInstanceWorld.id")] {{ currentInstanceWorld.name }}
div
span.x-link(v-text="currentInstanceWorld.authorName" @click="showUserDialog(currentInstanceWorld.authorId)" style="color:#909399;font-family:monospace")
div(style="margin-top:5px")
el-tag(v-if="currentInstanceWorld.$isLabs" type="primary" effect="plain" size="mini") Labs
el-tag(v-else-if="currentInstanceWorld.releaseStatus === 'public'" type="success" effect="plain" size="mini") Public
el-tag(v-else-if="currentInstanceWorld.releaseStatus === 'private'" type="danger" effect="plain" size="mini") Private
span(style="margin-left:5px")
span \#{{ currentInstanceLocation.instanceName }} {{ currentInstanceLocation.accessType }}
span.famfamfam-flags(v-if="currentInstanceLocation.region === 'eu'" class="europeanunion" style="display:inline-block;margin-left:5px")
span.famfamfam-flags(v-else-if="currentInstanceLocation.region === 'jp'" class="jp" style="display:inline-block;margin-left:5px")
span.famfamfam-flags(v-else class="us" style="display:inline-block;margin-left:5px")
span(v-if="lastLocation.playerList.size > 0" style="margin-left:5px")
| {{ lastLocation.playerList.size }}
| #[template(v-if="lastLocation.friendList.size > 0") ({{ lastLocation.friendList.size }})]
template(v-if="photonLobbyBots.length > 0")
|  
el-tooltip(placement="bottom")
template(#content)
span Photon Bot Id's: {{ photonLobbyBots.toString() }}
span(v-text="photonLobbyBots.length" style="color:red")
//- el-tag(type="info" effect="plain" size="mini" v-text="worldDialog.fileSize" style="margin-right:5px;margin-top:5px")
div(style="margin-top:5px")
span(v-show="currentInstanceWorld.name !== currentInstanceWorld.description" v-text="currentInstanceWorld.description" style="font-size:12px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2")
div.current-instance-table
data-tables(v-bind="currentInstanceUserList" @sort-change="updateTimers" @row-click="selectCurrentInstanceRow" style="margin-top:10px;cursor:pointer")
el-table-column(label="Avatar" width="60" prop="photo")
template(v-once #default="scope")
template(v-if="userImage(scope.row.ref)")
el-popover(placement="right" height="500px" trigger="hover")
img.friends-list-avatar(slot="reference" v-lazy="userImage(scope.row.ref)")
img.friends-list-avatar(v-lazy="userImageFull(scope.row.ref)" style="height:500px;cursor:pointer" @click="openExternalLink(userImageFull(scope.row.ref))")
el-table-column(label="Timer" width="90" prop="timer" sortable)
template(v-once #default="scope")
timer(:epoch="scope.row.timer")
el-table-column(label="Photon Id" width="100" prop="photonId" sortable)
template(v-once #default="scope")
span(v-text="scope.row.photonId")
el-table-column(label="Display Name" min-width="140" prop="ref.displayName")
template(v-once #default="scope")
span(v-text="scope.row.ref.displayName" :class="scope.row.ref.$trustColor")
el-table-column(label="Status" min-width="180" prop="ref.status")
template(v-once #default="scope")
template(v-if="scope.row.ref.status")
i.x-user-status(:class="statusClass(scope.row.ref.status)")
span
span(v-text="scope.row.ref.statusDescription")
el-table-column(label="Language" width="100" prop="ref.$languages")
template(v-once #default="scope")
el-tooltip(v-for="item in scope.row.ref.$languages" :key="item.key" placement="top")
template(#content)
span {{ item.value }} ({{ item.key }})
span.famfamfam-flags(:class="languageClass(item.key)" style="display:inline-block;margin-left:5px")
el-table-column(label="Bio Links" width="100" prop="ref.bioLinks")
template(v-once #default="scope")
el-tooltip(v-if="link" v-for="(link, index) in scope.row.ref.bioLinks" :key="index")
template(#content)
span(v-text="link")
img(:src="getFaviconUrl(link)" style="width:16px;height:16px;vertical-align:middle;margin-right:5px;cursor:pointer" @click.stop="openExternalLink(link)")
div.photon-event-table(v-if="photonEventTable.data.length > 0")
data-tables(v-bind="photonEventTable" style="margin-bottom:10px")
el-table-column(label="Date" prop="created_at" width="90")
template(v-once #default="scope")
el-tooltip(placement="right")
template(#content)
span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }}
span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }}
el-table-column(label="Name" prop="photonId" width="160")
template(v-once #default="scope")
span.x-link(v-text="getDisplayNameFromPhotonId(scope.row.photonId)" @click="showUserFromPhotonId(scope.row.photonId)" style="padding-right:10px")
el-table-column(label="Event" prop="text")
template(v-once #default="scope")
span(v-if="scope.row.avatar")
span ChangeAvatar
span.x-link(v-text="scope.row.avatar.name" @click="showAvatarDialog(scope.row.avatar.id)")
|  
span.avatar-info-public(v-if="scope.row.avatar.releaseStatus === 'public'") (Public)
span.avatar-info-own(v-else-if="scope.row.avatar.releaseStatus === 'private'") (Private)
template(v-if="scope.row.avatar.description && scope.row.avatar.name !== scope.row.avatar.description")
| - {{ scope.row.avatar.description }}
span(v-else v-text="scope.row.text")
//- feed
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'feed'")
data-tables(v-bind="feedTable" v-loading="feedTable.loading")
@@ -411,7 +504,7 @@ html
template(#tool)
div(style="margin:0 0 10px;display:flex;align-items:center")
el-select(v-model="playerModerationTable.filters[0].value" @change="saveTableFilters" multiple clearable collapse-tags style="flex:1" placeholder="Filter")
el-option(v-once v-for="type in ['block', 'mute', 'unmute', 'hideAvatar', 'showAvatar']" :key="type" :label="type" :value="type")
el-option(v-once v-for="type in ['block', 'unblock', 'mute', 'unmute', 'hideAvatar', 'showAvatar']" :key="type" :label="type" :value="type")
el-input(v-model="playerModerationTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px")
el-tooltip(placement="bottom" content="Refresh" :disabled="hideTooltips")
el-button(type="default" :loading="API.isPlayerModerationsLoading" @click="API.refreshPlayerModerations()" icon="el-icon-refresh" circle style="flex:none")
@@ -806,7 +899,7 @@ html
span.color-picker(slot="trigger") #[i.el-icon-s-open] Veteran User
div
v-swatches(v-model="trustColor.legendary" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-legendary")
span.color-picker(slot="trigger") #[i.el-icon-s-open] Early User
span.color-picker(slot="trigger") #[i.el-icon-s-open] Legend
div
v-swatches(v-model="trustColor.vip" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-vip")
span.color-picker(slot="trigger") #[i.el-icon-s-open] VRChat Team
@@ -977,6 +1070,33 @@ html
div.options-container-item
span.name Dance worlds only
el-switch(v-model="progressPieFilter" @change="changeYouTubeApi" :disabled="!openVR")
div.options-container
span.header Photon Logging Overlay
el-tooltip(placement="top" style="margin-left:5px" content="Requires '--log-debug-levels=API;NetworkData' steam launch option")
i.el-icon-warning
div.options-container-item
span.sub-header Photon Event HUD
div.options-container-item
span.name Enable
el-switch(v-model="photonEventOverlay" @change="saveEventOverlay" :disabled="!openVR")
div.options-container-item
span.name Filter
el-radio-group(v-model="photonEventOverlayFilter" @change="saveEventOverlay" size="mini" :disabled="!openVR || !photonEventOverlay")
el-radio-button(label="VIP")
el-radio-button(label="Friends")
el-radio-button(label="Everyone")
span.sub-header User timeout HUD
div.options-container-item
span.name Enable
el-switch(v-model="timeoutHudOverlay" @change="saveEventOverlay" :disabled="!openVR")
div.options-container-item
span.name Filter
el-radio-group(v-model="timeoutHudOverlayFilter" @change="saveEventOverlay" size="mini" :disabled="!openVR || !timeoutHudOverlay")
el-radio-button(label="VIP")
el-radio-button(label="Friends")
el-radio-button(label="Everyone")
div.options-container-item
el-button(size="small" icon="el-icon-time" @click="promptPhotonLobbyTimeoutThreshold" :disabled="!openVR") Timeout Threshold
div.options-container
span.header VRCX Instance Cache/Debug
div.options-container-item
@@ -1595,7 +1715,7 @@ html
el-dialog.x-dialog(ref="favoriteDialog" :visible.sync="favoriteDialog.visible" title="Choose Group" width="300px")
div(v-loading="favoriteDialog.loading")
el-button(v-for="group in favoriteDialog.groups" :key="group.name" style="display:block;width:100%;margin:10px 0" @click="addFavorite(group)" :disabled="group.count >= group.capacity") {{ group.displayName }} ({{ group.count }} / {{ group.capacity }})
//- dialog: invite
el-dialog.x-dialog(ref="inviteDialog" :visible.sync="inviteDialog.visible" title="Invite" width="450px")
div(v-loading="inviteDialog.loading")
@@ -2088,10 +2208,42 @@ html
el-radio-button(label="VIP")
el-radio-button(label="Friends")
el-radio-button(label="Everyone")
br
.toggle-item
span.toggle-name Photon Event Logging
el-tooltip(placement="top" style="margin-left:5px" content="Requires '--log-debug-levels=API;NetworkData' steam launch option")
i.el-icon-warning
.toggle-item
span.toggle-name Lobby Avatar Change
el-radio-group(v-model="sharedFeedFilters.noty.AvatarChange" size="mini")
el-radio-button(label="Off")
el-radio-button(label="VIP")
el-radio-button(label="Friends")
el-radio-button(label="Everyone")
.toggle-item
span.toggle-name Blocked
el-radio-group(v-model="sharedFeedFilters.noty.Blocked" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item
span.toggle-name Unblocked
el-radio-group(v-model="sharedFeedFilters.noty.Unblocked" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item
span.toggle-name Muted
el-radio-group(v-model="sharedFeedFilters.noty.Muted" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item
span.toggle-name Unmuted
el-radio-group(v-model="sharedFeedFilters.noty.Unmuted" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
template(#footer)
el-button(type="small" @click="cancelSharedFeedFilters") Cancel
el-button(type="primary" size="small" style="margin-left:10px" @click="saveSharedFeedFilters") Save
//- dialog: wrist feed filters
el-dialog.x-dialog(ref="wristFeedFiltersDialog" :visible.sync="wristFeedFiltersDialog.visible" title="Wrist Feed Filters" width="500px")
.toggle-list
@@ -2244,6 +2396,38 @@ html
el-radio-button(label="VIP")
el-radio-button(label="Friends")
el-radio-button(label="Everyone")
br
.toggle-item
span.toggle-name Photon Event Logging
el-tooltip(placement="top" style="margin-left:5px" content="Requires '--log-debug-levels=API;NetworkData' steam launch option")
i.el-icon-warning
.toggle-item
span.toggle-name Lobby Avatar Change
el-radio-group(v-model="sharedFeedFilters.wrist.AvatarChange" size="mini")
el-radio-button(label="Off")
el-radio-button(label="VIP")
el-radio-button(label="Friends")
el-radio-button(label="Everyone")
.toggle-item
span.toggle-name Blocked
el-radio-group(v-model="sharedFeedFilters.wrist.Blocked" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item
span.toggle-name Unblocked
el-radio-group(v-model="sharedFeedFilters.wrist.Unblocked" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item
span.toggle-name Muted
el-radio-group(v-model="sharedFeedFilters.wrist.Muted" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item
span.toggle-name Unmuted
el-radio-group(v-model="sharedFeedFilters.wrist.Unmuted" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
template(#footer)
el-button(type="small" @click="cancelSharedFeedFilters") Cancel
el-button(type="primary" size="small" @click="saveSharedFeedFilters") Save

View File

@@ -29,6 +29,9 @@ class Database {
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS ${Database.userPrefix}_notifications (id TEXT PRIMARY KEY, created_at TEXT, type TEXT, sender_user_id TEXT, sender_username TEXT, receiver_user_id TEXT, message TEXT, world_id TEXT, world_name TEXT, image_url TEXT, invite_message TEXT, request_message TEXT, response_message TEXT, expired INTEGER)`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS ${Database.userPrefix}_moderation (user_id TEXT PRIMARY KEY, updated_at TEXT, display_name TEXT, block INTEGER, mute INTEGER)`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS memos (user_id TEXT PRIMARY KEY, edited_at TEXT, memo TEXT)`
);
@@ -1104,6 +1107,59 @@ class Database {
}
return date;
}
async getModeration(input) {
var userId = input.replaceAll("'", '');
var row = {};
await sqliteService.execute((dbRow) => {
var block = false;
var mute = false;
if (dbRow[3] === 1) {
block = true;
}
if (dbRow[4] === 1) {
mute = true;
}
row = {
userId: dbRow[0],
updatedAt: dbRow[1],
displayName: dbRow[2],
block,
mute
};
}, `SELECT * FROM ${Database.userPrefix}_moderation WHERE user_id = '${userId}'`);
return row;
}
setModeration(entry) {
var block = 0;
var mute = 0;
if (entry.block) {
block = 1;
}
if (entry.mute) {
mute = 1;
}
sqliteService.executeNonQuery(
`INSERT OR REPLACE INTO ${Database.userPrefix}_moderation (user_id, updated_at, display_name, block, mute) VALUES (@user_id, @updated_at, @display_name, @block, @mute)`,
{
'@user_id': entry.userId,
'@updated_at': entry.updatedAt,
'@display_name': entry.displayName,
'@block': block,
'@mute': mute
}
);
}
deleteModeration(userId) {
sqliteService.executeNonQuery(
`DELETE FROM ${Database.userPrefix}_moderation WHERE user_id = @user_id`,
{
'@user_id': userId
}
);
}
}
var self = new Database();

View File

@@ -51,6 +51,15 @@ class GameLogService {
gameLog.url = args[0];
break;
case 'photon-event':
gameLog.json = args[0];
break;
case 'photon-id':
gameLog.displayName = args[0];
gameLog.photonId = args[1];
break;
default:
break;
}

View File

@@ -11,19 +11,10 @@ import locale from 'element-ui/lib/locale/lang/en';
import MarqueeText from 'vue-marquee-text-component';
Vue.component('marquee-text', MarqueeText);
import configRepository from './repository/config.js';
(async function () {
var $app = null;
await CefSharp.BindObjectAsync(
'AppApi',
'WebApi',
'SharedVariable',
'SQLite'
);
await configRepository.init();
await CefSharp.BindObjectAsync('AppApi');
Noty.overrideDefaults({
animation: {
@@ -170,6 +161,17 @@ import configRepository from './repository/config.js';
}
});
var removeFromArray = function (array, item) {
var {length} = array;
for (var i = 0; i < length; ++i) {
if (array[i] === item) {
array.splice(i, 1);
return true;
}
}
return false;
};
var $app = {
data: {
// 1 = 대시보드랑 손목에 보이는거
@@ -179,6 +181,7 @@ import configRepository from './repository/config.js';
cpuUsage: 0,
config: {},
downloadProgress: 0,
photonLobbyBotSize: 0,
nowPlaying: {
url: '',
name: '',
@@ -289,12 +292,18 @@ import configRepository from './repository/config.js';
$app.methods.configUpdate = function (json) {
this.config = JSON.parse(json);
this.hudFeed = [];
this.hudTimeout = [];
};
$app.methods.updateDownloadProgress = function (progress) {
this.downloadProgress = parseInt(progress, 10);
};
$app.methods.updatePhotonLobbyBotSize = function (size) {
this.photonLobbyBotSize = parseInt(size, 10);
};
$app.methods.nowPlayingUpdate = function (json) {
this.nowPlaying = JSON.parse(json);
if (this.appType === '2') {
@@ -356,7 +365,7 @@ import configRepository from './repository/config.js';
var text = '';
var img = '';
if (image) {
img = `<img class="noty-img" src="data:image/png;base64, ${image}"></img>`;
img = `<img class="noty-img" src="${image}"></img>`;
}
switch (noty.type) {
case 'OnPlayerJoined':
@@ -455,6 +464,18 @@ import configRepository from './repository/config.js';
case 'MutedOnPlayerLeft':
text = `Muted user <strong>${noty.displayName}</strong> has left`;
break;
case 'Blocked':
text = `<strong>${noty.displayName}</strong> has blocked you`;
break;
case 'Unblocked':
text = `<strong>${noty.displayName}</strong> has unblocked you`;
break;
case 'Muted':
text = `<strong>${noty.displayName}</strong> has muted you`;
break;
case 'Unmuted':
text = `<strong>${noty.displayName}</strong> has unmuted you`;
break;
default:
break;
}
@@ -510,6 +531,61 @@ import configRepository from './repository/config.js';
Noty.closeAll();
};
$app.data.hudFeed = [];
$app.data.cleanHudFeedLoopStatus = false;
$app.methods.cleanHudFeedLoop = function () {
if (!this.cleanHudFeedLoopStatus) {
return;
}
this.cleanHudFeed();
if (this.hudFeed.length === 0) {
this.cleanHudFeedLoopStatus = false;
return;
}
setTimeout(() => this.cleanHudFeedLoop(), 500);
};
$app.methods.cleanHudFeed = function () {
var dt = Date.now();
this.hudFeed.forEach((item) => {
if (item.time + 6000 < dt) {
removeFromArray(this.hudFeed, item);
}
});
if (this.hudFeed.length > 10) {
this.hudFeed.length = 10;
}
if (!this.cleanHudFeedLoopStatus) {
this.cleanHudFeedLoopStatus = true;
this.cleanHudFeedLoop();
}
};
$app.methods.addEntryHudFeed = function (json) {
var {displayName, text} = JSON.parse(json);
var combo = 1;
this.hudFeed.forEach((item) => {
if (item.displayName === displayName && item.text === text) {
combo = item.combo + 1;
removeFromArray(this.hudFeed, item);
}
});
this.hudFeed.unshift({
time: Date.now(),
displayName,
text,
combo
});
this.cleanHudFeed();
};
$app.data.hudTimeout = [];
$app.methods.updateHudTimeout = function (json) {
this.hudTimeout = JSON.parse(json);
};
$app = new Vue($app);
window.$app = $app;
})();

View File

@@ -397,6 +397,7 @@ html
span(style="float:right") {{ lastLocationTimer }}
span(style="display:inline-block") {{ lastLocation.playerList.length }}
span(style="display:inline-block;font-weight:bold") {{ lastLocation.friendList.length !== 0 ? ` (${lastLocation.friendList.length})` : ''}}
span(v-if="photonLobbyBotSize > 0 && lastLocation.playerList.length > 0" style="display:inline-block;color:red;margin-left:5px") {{ photonLobbyBotSize }}
template(v-else)
template(v-if="downloadProgress === 100")
span(style="display:inline-block;margin-right:5px") Downloading: #[i.el-icon-loading]
@@ -406,11 +407,24 @@ html
span(style="float:right") Timer: {{ lastLocationTimer }}
span(style="display:inline-block") Players: {{ lastLocation.playerList.length }}
span(style="display:inline-block;font-weight:bold") {{ lastLocation.friendList.length !== 0 ? ` (${lastLocation.friendList.length})` : ''}}
span(v-if="photonLobbyBotSize > 0 && lastLocation.playerList.length > 0" style="display:inline-block;color:red;margin-left:5px") {{ photonLobbySize }}
br
span(style="float:right") {{ currentTime | formatDate('YYYY-MM-DD HH:MI:SS AMPM') }}
span CPU {{ cpuUsage }}%
template(v-else)
svg(class="np-progress-circle")
circle(class="np-progress-circle-stroke" cx="60" cy="60" stroke="white" r="30" fill="transparent" stroke-width="60")
.hud-feed
div(v-for="feed in hudFeed")
.item <strong>{{ feed.displayName }}</strong> {{ feed.text }}
template(v-if="feed.combo > 1")
span.combo x{{ feed.combo }}
.hud-timeout(v-if="hudTimeout.length > 0")
.hud-timeout-feed
div(v-for="feed in hudTimeout")
p.item ({{ feed.time }}s) {{ feed.displayName }}
svg(version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve")
path(fill="#ED1B24" d="M68.6,96.5L87,78.1c1.6-1.6,1.6-4.1,0-5.7s-4.1-1.6-5.7,0L62.9,90.9L44.5,72.5l18.4-18.4c1.6-1.6,1.6-4.1,0-5.7c-1.6-1.6-4.1-1.6-5.7,0L38.9,66.8l-6.4-6.4L21.2,71.8C11,82,9.7,97.9,17.4,109.5L0,126.9l8.5,8.5L25.9,118c11.6,7.7,27.5,6.4,37.8-3.8L75,102.9C75,102.9,68.6,96.5,68.6,96.5z")
path(fill="#ED1B24" d="M102.9,75l11.3-11.3c10.3-10.3,11.5-26.1,3.8-37.8l17.4-17.4L126.9,0l-17.4,17.4C97.9,9.7,82,11,71.8,21.2L60.5,32.5C102,74,60.8,32.9,102.9,75z")
script(src="vendor.js")
script(src="vr.js")

View File

@@ -179,8 +179,8 @@ button {
font-family: 'Noto Sans JP', 'Noto Sans KR', 'Meiryo UI', 'Malgun Gothic',
'Segoe UI', sans-serif;
line-height: normal;
text-shadow: #000 0px 0px 2px, #000 0px 0px 2px, #000 0px 0px 2px,
#000 0px 0px 2px, #000 0px 0px 2px, #000 0px 0px 2px;
text-shadow: #000 0px 0px 3px, #000 0px 0px 3px, #000 0px 0px 3px,
#000 0px 0px 3px, #000 0px 0px 3px, #000 0px 0px 3px;
}
.x-app {
@@ -350,3 +350,49 @@ i.x-user-status.busy {
transform: rotate(359deg);
}
}
.hud-feed {
position: absolute;
right: 0;
width: 100%;
}
.hud-feed .item,
.hud-timeout .item {
margin: 0;
text-align: right;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.hud-feed .item {
font-size: 32px;
}
.hud-feed .combo {
color: #aaa;
}
.hud-timeout .item {
font-size: 40px;
}
.hud-timeout {
position: absolute;
bottom: 0;
right: 0;
}
.hud-timeout-feed {
position: absolute;
bottom: 150px;
right: 0;
color: #ed1b24;
}
.hud-timeout svg {
position: absolute;
right: -160px;
bottom: 0;
}