Skip to content

Commit 6679900

Browse files
committed
Merge branch 'pyro' of github.com:/modrinth/code into pyro
2 parents 24aea4e + 0c4c5d9 commit 6679900

File tree

21 files changed

+405
-215
lines changed

21 files changed

+405
-215
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/app-frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@modrinth/app-frontend",
33
"private": true,
4-
"version": "0.8.8",
4+
"version": "0.8.9",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

apps/app/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "theseus_gui"
3-
version = "0.8.8"
3+
version = "0.8.9"
44
description = "The Modrinth App is a desktop application for managing your Minecraft mods"
55
license = "GPL-3.0-only"
66
repository = "https://github.com/modrinth/code/apps/app/"

apps/app/src/api/ads-init.js

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -21,86 +21,3 @@ document.addEventListener(
2121
window.open = (url, target, features) => {
2222
window.top.postMessage({ modrinthOpenUrl: url }, 'https://modrinth.com')
2323
}
24-
25-
function muteAudioContext() {
26-
if (window.AudioContext || window.webkitAudioContext) {
27-
const AudioContext = window.AudioContext || window.webkitAudioContext
28-
const originalCreateMediaElementSource = AudioContext.prototype.createMediaElementSource
29-
const originalCreateMediaStreamSource = AudioContext.prototype.createMediaStreamSource
30-
const originalCreateMediaStreamTrackSource = AudioContext.prototype.createMediaStreamTrackSource
31-
const originalCreateBufferSource = AudioContext.prototype.createBufferSource
32-
const originalCreateOscillator = AudioContext.prototype.createOscillator
33-
34-
AudioContext.prototype.createGain = function () {
35-
const gain = originalCreateGain.call(this)
36-
gain.gain.value = 0
37-
return gain
38-
}
39-
40-
AudioContext.prototype.createMediaElementSource = function (mediaElement) {
41-
const source = originalCreateMediaElementSource.call(this, mediaElement)
42-
source.connect(this.createGain())
43-
return source
44-
}
45-
46-
AudioContext.prototype.createMediaStreamSource = function (mediaStream) {
47-
const source = originalCreateMediaStreamSource.call(this, mediaStream)
48-
source.connect(this.createGain())
49-
return source
50-
}
51-
52-
AudioContext.prototype.createMediaStreamTrackSource = function (mediaStreamTrack) {
53-
const source = originalCreateMediaStreamTrackSource.call(this, mediaStreamTrack)
54-
source.connect(this.createGain())
55-
return source
56-
}
57-
58-
AudioContext.prototype.createBufferSource = function () {
59-
const source = originalCreateBufferSource.call(this)
60-
source.connect(this.createGain())
61-
return source
62-
}
63-
64-
AudioContext.prototype.createOscillator = function () {
65-
const oscillator = originalCreateOscillator.call(this)
66-
oscillator.connect(this.createGain())
67-
return oscillator
68-
}
69-
}
70-
}
71-
72-
function muteVideo(mediaElement) {
73-
let count = Number(mediaElement.getAttribute('data-modrinth-muted-count') ?? 0)
74-
75-
if (!mediaElement.muted || mediaElement.volume !== 0) {
76-
mediaElement.muted = true
77-
mediaElement.volume = 0
78-
79-
mediaElement.setAttribute('data-modrinth-muted-count', count + 1)
80-
}
81-
82-
if (count > 5) {
83-
// Video is detected as malicious, so it is removed from the page
84-
mediaElement.remove()
85-
}
86-
}
87-
88-
function muteVideos() {
89-
document.querySelectorAll('video, audio').forEach(function (mediaElement) {
90-
muteVideo(mediaElement)
91-
92-
if (!mediaElement.hasAttribute('data-modrinth-muted')) {
93-
mediaElement.addEventListener('volumechange', () => muteVideo(mediaElement))
94-
95-
mediaElement.setAttribute('data-modrinth-muted', 'true')
96-
}
97-
})
98-
}
99-
100-
document.addEventListener('DOMContentLoaded', () => {
101-
muteVideos()
102-
muteAudioContext()
103-
104-
const observer = new MutationObserver(muteVideos)
105-
observer.observe(document.body, { childList: true, subtree: true })
106-
})

apps/app/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
]
5050
},
5151
"productName": "Modrinth App",
52-
"version": "0.8.8",
52+
"version": "0.8.9",
5353
"mainBinaryName": "Modrinth App",
5454
"identifier": "ModrinthApp",
5555
"plugins": {

apps/frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@formatjs/cli": "^6.2.12",
1717
"@nuxt/devtools": "^1.3.3",
1818
"@nuxtjs/turnstile": "^0.8.0",
19+
"@types/dompurify": "^3.0.5",
1920
"@types/node": "^20.1.0",
2021
"@vintl/compact-number": "^2.0.5",
2122
"@vintl/how-ago": "^3.0.1",

apps/frontend/src/components/ui/AdPlaceholder.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ onMounted(() => {
5757
{
5858
divId: "modrinth-rail-1",
5959
baseDivId: "pb-slot-square-2",
60+
targeting: {
61+
location: "web",
62+
},
6063
},
6164
]);
6265
});

apps/frontend/src/components/ui/servers/LogParser.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,14 @@ const parsedLog = computed(() => {
4747
});
4848
4949
const sanitizedLog = computed(() => {
50-
return DOMPurify.sanitize(parsedLog.value);
50+
return DOMPurify.sanitize(parsedLog.value, {
51+
ALLOWED_TAGS: ["span"],
52+
ALLOWED_ATTR: ["style"],
53+
ALLOWED_CSS_STYLES: {
54+
color: true,
55+
"background-color": true,
56+
},
57+
});
5158
});
5259
</script>
5360

apps/frontend/src/components/ui/servers/PanelTerminal.vue

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,12 @@
2626
<div
2727
class="absolute -bottom-2 -right-2 h-7 w-7"
2828
:style="{
29-
// background should be solid red bottom right corner of a circle
3029
background: `radial-gradient(circle at 0% 0%, transparent 50%, var(--color-raised-bg) 52%)`,
3130
}"
3231
></div>
3332
<div
3433
class="absolute -bottom-2 -left-2 h-7 w-7"
3534
:style="{
36-
// background should be solid red bottom right corner of a circle
3735
background: `radial-gradient(circle at 100% 0%, transparent 50%, var(--color-raised-bg) 52%)`,
3836
}"
3937
></div>
@@ -118,6 +116,8 @@ const clientHeight = ref(0);
118116
const isFullScreen = ref(props.fullScreen);
119117
120118
const initial = ref(false);
119+
const userHasScrolled = ref(false);
120+
const isScrolledToBottom = ref(true);
121121
122122
const totalHeight = computed(
123123
() =>
@@ -126,7 +126,6 @@ const totalHeight = computed(
126126
);
127127
128128
watch(totalHeight, () => {
129-
console.log(initial.value);
130129
if (!initial.value) {
131130
scrollToBottom();
132131
}
@@ -136,7 +135,7 @@ watch(totalHeight, () => {
136135
const lerp = (start: number, end: number, t: number) => start * (1 - t) + end * t;
137136
138137
const getBlurStyle = (i: number) => {
139-
const properBlurIteration = i + 1; // Adjusting iteration for reverse order
138+
const properBlurIteration = i + 1;
140139
const blur = lerp(0, 2 ** (properBlurIteration - 3), bottomThreshold.value);
141140
const singular = 100 / progressiveBlurIterations.value;
142141
let mask = "linear-gradient(";
@@ -162,9 +161,10 @@ const getBlurStyle = (i: number) => {
162161
backdropFilter: `blur(${blur}px)`,
163162
mask,
164163
position: "absolute" as any,
165-
zIndex: progressiveBlurIterations.value - i, // Adjusting z-index for reverse order
164+
zIndex: progressiveBlurIterations.value - i,
166165
};
167166
};
167+
168168
const getItemOffset = (index: number) => {
169169
return itemHeights.value.slice(0, index).reduce((sum, height) => sum + height, 0);
170170
};
@@ -205,11 +205,16 @@ const handleScroll = () => {
205205
if (scrollContainer.value) {
206206
scrollTop.value = scrollContainer.value.scrollTop;
207207
clientHeight.value = scrollContainer.value.clientHeight;
208+
209+
const scrollHeight = scrollContainer.value.scrollHeight;
210+
isScrolledToBottom.value = scrollTop.value + clientHeight.value >= scrollHeight - 32; // threshold
211+
212+
if (!isScrolledToBottom.value) {
213+
userHasScrolled.value = true;
214+
}
208215
}
209216
210217
const maxBottom = 256;
211-
// when we're at the very bottom of the scroll container, bottomThreshold should be 0
212-
// when we're maxBottom pixels from the bottom of the scroll container, bottomThreshold should be 1
213218
bottomThreshold.value = Math.min(
214219
1,
215220
((scrollContainer.value?.scrollHeight || 1) - scrollTop.value - clientHeight.value) / maxBottom,
@@ -242,6 +247,8 @@ const updateClientHeight = () => {
242247
const scrollToBottom = () => {
243248
if (scrollContainer.value) {
244249
scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight + 99999999;
250+
userHasScrolled.value = false;
251+
isScrolledToBottom.value = true;
245252
}
246253
};
247254
@@ -303,7 +310,9 @@ watch(
303310
304311
nextTick(() => {
305312
updateItemHeights();
306-
scrollToBottom();
313+
if (!userHasScrolled.value || isScrolledToBottom.value) {
314+
scrollToBottom();
315+
}
307316
});
308317
},
309318
{ deep: true, immediate: true },
@@ -329,6 +338,7 @@ watch(isFullScreen, () => {
329338
});
330339
});
331340
</script>
341+
332342
<style scoped>
333343
.terminal-font {
334344
font-family: var(--mono-font);
Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,42 @@
11
<template>
2-
<div
3-
class="save-banner fixed bottom-8 left-4 right-4 z-50 mx-auto h-fit w-full max-w-4xl rounded-2xl border-2 border-solid border-divider bg-bg-raised p-4 transition-all duration-300"
4-
>
5-
<div class="flex flex-col items-center justify-between gap-2 md:flex-row">
6-
<span class="font-bold text-contrast">Careful, you have unsaved changes!</span>
7-
<div class="flex gap-2">
8-
<Button transparent :loading="props.isUpdating" @click="props.reset"> Reset </Button>
9-
<Button color="primary" :loading="props.isUpdating" @click="props.save"> Save </Button>
10-
<Button
11-
v-if="props.restart"
12-
color="primary"
13-
:loading="props.isUpdating"
14-
@click="saveAndRestart"
15-
>
16-
Save & Restart
17-
</Button>
2+
<transition name="save-banner">
3+
<div
4+
v-if="props.isVisible"
5+
data-pyro-save-banner
6+
class="save-banner fixed bottom-8 left-4 right-4 z-50 mx-auto h-fit w-full max-w-4xl rounded-2xl border-2 border-solid border-divider bg-bg-raised p-4 transition-all duration-300"
7+
>
8+
<div class="flex flex-col items-center justify-between gap-2 md:flex-row">
9+
<span class="font-bold text-contrast">Careful, you have unsaved changes!</span>
10+
<div class="flex gap-2">
11+
<ButtonStyled type="transparent" color="standard" transparent>
12+
<button :disabled="props.isUpdating" @click="props.reset">Reset</button>
13+
</ButtonStyled>
14+
<ButtonStyled type="standard" color="brand">
15+
<button :disabled="props.isUpdating" @click="props.save">
16+
{{ props.isUpdating ? "Saving..." : "Save" }}
17+
</button>
18+
</ButtonStyled>
19+
<ButtonStyled v-if="props.restart" type="standard" color="brand">
20+
<button :disabled="props.isUpdating" @click="saveAndRestart">
21+
{{ props.isUpdating ? "Saving..." : "Save & Restart" }}
22+
</button>
23+
</ButtonStyled>
24+
</div>
1825
</div>
1926
</div>
20-
</div>
27+
</transition>
2128
</template>
2229

2330
<script setup lang="ts">
24-
import { Button } from "@modrinth/ui";
31+
import { ButtonStyled } from "@modrinth/ui";
2532
import type { Server } from "~/composables/pyroServers";
2633
2734
const props = defineProps<{
2835
isUpdating: boolean;
2936
restart?: boolean;
3037
save: () => void;
3138
reset: () => void;
39+
isVisible: boolean;
3240
server: Server<["general", "mods", "backups", "network", "startup", "ws", "fs"]>;
3341
}>();
3442
@@ -39,18 +47,60 @@ const saveAndRestart = async () => {
3947
</script>
4048

4149
<style scoped>
42-
.save-banner {
43-
animation: slide-up 200ms ease;
50+
.save-banner-enter-active {
51+
transition:
52+
opacity 800ms,
53+
transform 800ms;
4454
}
4555
46-
@keyframes slide-up {
47-
from {
48-
opacity: 0;
49-
transform: translateY(100%);
50-
}
51-
to {
52-
opacity: 1;
53-
transform: translateY(0);
54-
}
56+
.save-banner-leave-active {
57+
transition:
58+
opacity 200ms,
59+
transform 200ms;
60+
}
61+
62+
.save-banner-enter-active {
63+
transition-timing-function: linear(
64+
0 0%,
65+
0.01 0.8%,
66+
0.04 1.6%,
67+
0.161 3.3%,
68+
0.816 9.4%,
69+
1.046 11.9%,
70+
1.189 14.4%,
71+
1.231 15.7%,
72+
1.254 17%,
73+
1.259 17.8%,
74+
1.257 18.6%,
75+
1.236 20.45%,
76+
1.194 22.3%,
77+
1.057 27%,
78+
0.999 29.4%,
79+
0.955 32.1%,
80+
0.942 33.5%,
81+
0.935 34.9%,
82+
0.933 36.65%,
83+
0.939 38.4%,
84+
1 47.3%,
85+
1.011 49.95%,
86+
1.017 52.6%,
87+
1.016 56.4%,
88+
1 65.2%,
89+
0.996 70.2%,
90+
1.001 87.2%,
91+
1 100%
92+
);
93+
}
94+
95+
.save-banner-enter-from,
96+
.save-banner-leave-to {
97+
opacity: 0;
98+
transform: translateY(100%) scale(0.98);
99+
}
100+
101+
.save-banner-enter-to,
102+
.save-banner-leave-from {
103+
opacity: 1;
104+
transform: none;
55105
}
56106
</style>

0 commit comments

Comments
 (0)