From 6a72315a2156873fcc9f4ad70dc04a666e7d9688 Mon Sep 17 00:00:00 2001
From: maridat47
Date: Tue, 1 Sep 2026 23:59:04 +0300
Subject: [PATCH] Adding some small features
---
.env.example | 4 +--
.gitignore | 1 +
README.md | 2 +-
apps/web/public/generic/css/theme.css | 6 ++++
apps/web/public/generic/js/base.js | 28 +++++++++++++++++++
apps/web/views/layout.ejs | 6 ++--
apps/web/views/partials/beatmap/card.ejs | 25 +++++++++++++----
.../web/views/partials/profile/basicStats.ejs | 6 +++-
.../partials/profile/cardRecentPasses.ejs | 6 ++--
ecosystem.config.js | 4 +--
lib/utils.js | 12 ++++++++
11 files changed, 83 insertions(+), 17 deletions(-)
diff --git a/.env.example b/.env.example
index 6abaa04..8b64402 100644
--- a/.env.example
+++ b/.env.example
@@ -34,8 +34,8 @@ WEBSERVER_PORT=
# The hostname/port where the webserver is accessible (Default: localhost:8080)
HOST=
-# Whether or not the host is using HTTPS (Default: true)
-HTTPS=
+# Whether or not the host is using HTTPS (Default: true) KAYLA PLS SET THIS TO FALSE AS DEFAULT
+HTTPS=false
# Webserver Rate Limits
diff --git a/.gitignore b/.gitignore
index 672cfaa..1d05886 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
.*
!.env.example
+|.env
!.gitignore
!.vscode
!.github
diff --git a/README.md b/README.md
index 6d1f5ff..9867db6 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ Note that the intent of these instructions is NOT to teach you how to run your o
2. Run `npm run maintain -- importmaps` to read the dump and fetch map data from the osu! API (this will take a few hours)
9. Start the processes
- **Recommended:** Install [PM2](https://pm2.io/) with `npm i -g pm2` and then run `pm2 start` to start both the webserver and updater in the background
- - If you choose to forgo PM2, you can start each process using `npm start webserver` or `npm start updater`
+ - If you choose to forgo PM2, you can start each process using `npm run webserver` or `npm run updater`
**If you need user authentication working:**
diff --git a/apps/web/public/generic/css/theme.css b/apps/web/public/generic/css/theme.css
index d80fa17..bcf137d 100644
--- a/apps/web/public/generic/css/theme.css
+++ b/apps/web/public/generic/css/theme.css
@@ -542,6 +542,12 @@ img {
padding: 0px 8px;
}
+.btn.little {
+ height: 20px;
+ font-size: 12px;
+ padding: 0px 4px;
+}
+
.btn.small .icon {
--size: 16px;
}
diff --git a/apps/web/public/generic/js/base.js b/apps/web/public/generic/js/base.js
index 53a4b21..9e443b2 100644
--- a/apps/web/public/generic/js/base.js
+++ b/apps/web/public/generic/js/base.js
@@ -305,6 +305,34 @@ const initCustomTooltips = () => {
},
{ capture: true, passive: true }
);
+
+ // More conditions to hide tooltip when it behaves annoyingly
+ // by maridat47
+ window.addEventListener('click', e => {
+ const target = e.target.closest('[data-tooltip]');
+ if (target && target !== currentTarget) {
+ tooltip.classList.remove('visible');
+
+ // 3. CLEAN UP POPOVER
+ // Wait for CSS transition (opacity) to finish before removing from DOM
+ setTimeout(() => {
+ if (!tooltip.classList.contains('visible')) {
+ tooltip.hidePopover();
+ }
+ }, 200);
+
+ currentTarget = null;
+ }
+ });
+
+ window.addEventListener('keydown', (event) => {
+ if (event.key === 'Escape' && tooltip.classList.contains('visible')) {
+ tooltip.classList.remove('visible');
+ tooltip.hidePopover();
+ currentTarget = null;
+ }
+ }
+ );
};
// Handle image load states
diff --git a/apps/web/views/layout.ejs b/apps/web/views/layout.ejs
index d430fb9..17682d8 100644
--- a/apps/web/views/layout.ejs
+++ b/apps/web/views/layout.ejs
@@ -223,12 +223,10 @@
Privacy Policy
Running commit
-
- <%= locals?.git?.hash %>
-
+ <%= locals?.git?.hash %>
deployed <%= dayjs(locals?.git?.date).utc().format('YYYY-MM-DD HH:mm:ss [UTC]') %>
diff --git a/apps/web/views/partials/beatmap/card.ejs b/apps/web/views/partials/beatmap/card.ejs
index 6c75803..505c142 100644
--- a/apps/web/views/partials/beatmap/card.ejs
+++ b/apps/web/views/partials/beatmap/card.ejs
@@ -20,11 +20,20 @@
loading="lazy" />
-
- <%= beatmap.beatmapset.title %>
+
+
+ <%= beatmap.beatmapset.title %>
+
+
<%= beatmap.xp %> cxp
+ •
+
+ <%= utils.formatDate(beatmap.beatmapset.time_ranked) %>
+
diff --git a/apps/web/views/partials/profile/basicStats.ejs b/apps/web/views/partials/profile/basicStats.ejs
index e4d74cd..01189e7 100644
--- a/apps/web/views/partials/profile/basicStats.ejs
+++ b/apps/web/views/partials/profile/basicStats.ejs
@@ -9,7 +9,11 @@
completion xp
-
<%= n(stats.xp) %>
+
+ <%= n(stats.xp) %>
+
% completed
diff --git a/apps/web/views/partials/profile/cardRecentPasses.ejs b/apps/web/views/partials/profile/cardRecentPasses.ejs
index 507881a..ebad8e2 100644
--- a/apps/web/views/partials/profile/cardRecentPasses.ejs
+++ b/apps/web/views/partials/profile/cardRecentPasses.ejs
@@ -31,8 +31,10 @@
safelyRunOnLoad(loadMonthlyGraph);
-
-
+ <% const recentXp = recentPasses.reduce((sum, pass) => sum + pass.beatmap.xp, 0)
+ const recentLength = recentPasses.reduce((sum, pass) => sum + pass.beatmap.duration_secs, 0) %>
+
+
<% if (recentPasses.length === 0) { %>
diff --git a/ecosystem.config.js b/ecosystem.config.js
index 5c9dade..271fdd5 100644
--- a/ecosystem.config.js
+++ b/ecosystem.config.js
@@ -3,7 +3,7 @@ module.exports = {
apps: [
{
name: 'oc-web',
- script: 'npm',
+ script: './apps/web/index.js',
args: 'run webserver',
cwd: './',
watch: [...globalWatch, 'apps/web/index.js', 'apps/web/routes'],
@@ -11,7 +11,7 @@ module.exports = {
},
{
name: 'oc-updater',
- script: 'npm',
+ script: './apps/updater/index.js',
args: 'run updater',
cwd: './',
watch: [...globalWatch, 'apps/updater']
diff --git a/lib/utils.js b/lib/utils.js
index eab87fe..31f5008 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -383,6 +383,18 @@ const utils = {
});
},
+ formatDate: (secs, withTime = false, withUtc = false) => {
+ const rawDate = new Date(secs).toISOString().split('T');
+ const date = rawDate[0];
+ const time = rawDate[1].split('.')[0];
+ return date + (withTime ? ', ' + time : '') + (withUtc ? ' UTC' : '');
+ },
+
+ capitalize: (text) => {
+ if (!text) return text;
+ return text[0].toUpperCase() + text.slice(1);
+ },
+
sanitizeFtsQuery: input => {
// Thanks Gemini
if (!input || !input.trim()) return null;