Skip to content

Commit

Permalink
Servers console performance improvements (#3007)
Browse files Browse the repository at this point in the history
* feat: init selecting paper+purpur on purchase flow

Signed-off-by: Evan Song <[email protected]>

* feat: properly implement Paper/Purpur in Platform

Signed-off-by: Evan Song <[email protected]>

* chore: correct wording

Signed-off-by: Evan Song <[email protected]>

* feat: redo platform modal

Signed-off-by: Evan Song <[email protected]>

* Switch to HCaptcha for Auth-related captchas (#2945)

* Switch to HCaptcha for Auth-related captchas

* run fmt

* fix hcaptcha not loading

* fix: more robust loader dropdown logic

Signed-off-by: Evan Song <[email protected]>

* fix: handle "not yet supported" install err

Signed-off-by: Evan Song <[email protected]>

* chore: fix icon kerfuffles

Signed-off-by: Evan Song <[email protected]>

* chore: improve vanilla install modal title

Signed-off-by: Evan Song <[email protected]>

* fix: spacing

Signed-off-by: Evan Song <[email protected]>

* feat: usePyroConsole store instead of passing a prop to prevent bulk panel refreshing

* chore: improve no loader state

Signed-off-by: Evan Song <[email protected]>

* fix: type error

Signed-off-by: Evan Song <[email protected]>

* chore: adjust mod version modal title

Signed-off-by: Evan Song <[email protected]>

* chore: adjust modpack warning copy

Signed-off-by: Evan Song <[email protected]>

* feat: vanilla empty state in content page

Signed-off-by: Evan Song <[email protected]>

* chore: adjust copy

Signed-off-by: Evan Song <[email protected]>

* chore: update icon

Signed-off-by: Evan Song <[email protected]>

* fix: loader type

Signed-off-by: Evan Song <[email protected]>

* fix: loader type

Signed-off-by: Evan Song <[email protected]>

* feat: always show dropdown if possible

Signed-off-by: Evan Song <[email protected]>

* chore: improve spacing

Signed-off-by: Evan Song <[email protected]>

* chore: appear disabled

Signed-off-by: Evan Song <[email protected]>

* h

Signed-off-by: Evan Song <[email protected]>

* chore: if reinstalling, show it on the modal title

Signed-off-by: Evan Song <[email protected]>

* feat: put it in the dropdown, they said

Signed-off-by: Evan Song <[email protected]>

* chore: adjust style

Signed-off-by: Evan Song <[email protected]>

* chore: sort paper-purpur versions desc

Signed-off-by: Evan Song <[email protected]>

* fix: do not consider backup limit in reinstall prompt

Signed-off-by: Evan Song <[email protected]>

* feat: backup locking, plugin support

* fix: content type error

Signed-off-by: Evan Song <[email protected]>

* fix: casing

Signed-off-by: Evan Song <[email protected]>

* fix: plugins pt 2

* feat: backups, mrpack

* fix: type errors come on

Signed-off-by: Evan Song <[email protected]>

* fix: spacing

Signed-off-by: Evan Song <[email protected]>

* fix: type maxing

* chore: show copy button on allocation rows

Signed-off-by: Evan Song <[email protected]>

* feat: suspend improvement

---------

Signed-off-by: Evan Song <[email protected]>
Co-authored-by: Geometrically <[email protected]>
Co-authored-by: Jai A <[email protected]>
Co-authored-by: TheWander02 <[email protected]>
  • Loading branch information
4 people authored Dec 11, 2024
1 parent 742c0ed commit 6ec1dcf
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 26 deletions.
21 changes: 12 additions & 9 deletions apps/frontend/src/components/ui/servers/PanelTerminal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,18 @@
<script setup lang="ts">
import { RightArrowIcon } from "@modrinth/assets";
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from "vue";
import { usePyroConsole } from "~/store/console.ts";
const { $cosmetics } = useNuxtApp();
const cosmetics = $cosmetics;
const props = defineProps<{
consoleOutput: string[];
fullScreen: boolean;
}>();
const pyroConsole = usePyroConsole();
const consoleOutput = pyroConsole.output;
const scrollContainer = ref<HTMLElement | null>(null);
const itemHeights = ref<number[]>([]);
const averageItemHeight = ref(36);
Expand All @@ -170,7 +173,7 @@ const handleScrollEvent = () => {
const totalHeight = computed(
() =>
itemHeights.value.reduce((sum, height) => sum + height, 0) ||
props.consoleOutput.length * averageItemHeight.value,
consoleOutput.value.length * averageItemHeight.value,
);
watch(totalHeight, () => {
Expand Down Expand Up @@ -223,7 +226,7 @@ const visibleStartIndex = computed(() => {
let index = 0;
let offset = 0;
while (
index < props.consoleOutput.length &&
index < consoleOutput.value.length &&
offset < scrollTop.value - bufferSize * averageItemHeight.value
) {
offset += itemHeights.value[index] || averageItemHeight.value;
Expand All @@ -236,17 +239,17 @@ const visibleEndIndex = computed(() => {
let index = visibleStartIndex.value;
let offset = getItemOffset(index);
while (
index < props.consoleOutput.length &&
index < consoleOutput.value.length &&
offset < scrollTop.value + clientHeight.value + bufferSize * averageItemHeight.value
) {
offset += itemHeights.value[index] || averageItemHeight.value;
index++;
}
return Math.min(props.consoleOutput.length - 1, index);
return Math.min(consoleOutput.value.length - 1, index);
});
const visibleItems = computed(() =>
props.consoleOutput.slice(visibleStartIndex.value, visibleEndIndex.value + 1),
consoleOutput.value.slice(visibleStartIndex.value, visibleEndIndex.value + 1),
);
const offsetY = computed(() => getItemOffset(visibleStartIndex.value));
Expand Down Expand Up @@ -280,7 +283,7 @@ const updateItemHeights = async () => {
const index = visibleStartIndex.value + idx;
const height = el.getBoundingClientRect().height;
itemHeights.value[index] = height;
const content = props.consoleOutput[index];
const content = consoleOutput.value[index];
if (content) {
cachedHeights.value.set(content, height);
}
Expand Down Expand Up @@ -457,7 +460,7 @@ const initializeTerminal = async () => {
updateClientHeight();
const initialHeights = props.consoleOutput.map(
const initialHeights = consoleOutput.value.map(
(content) => cachedHeights.value.get(content) || averageItemHeight.value,
);
itemHeights.value = initialHeights;
Expand Down Expand Up @@ -487,7 +490,7 @@ onUnmounted(() => {
});
watch(
() => props.consoleOutput,
() => consoleOutput.value,
async (newOutput) => {
const newItemsCount = newOutput.length - itemHeights.value.length;
Expand Down
23 changes: 8 additions & 15 deletions apps/frontend/src/pages/servers/manage/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,6 @@
:stats="stats"
:server-power-state="serverPowerState"
:power-state-details="powerStateDetails"
:console-output="throttledConsoleOutput"
:socket="socket"
:server="server"
@reinstall="onReinstall"
Expand All @@ -302,9 +301,9 @@ import {
} from "@modrinth/assets";
import DOMPurify from "dompurify";
import { ButtonStyled } from "@modrinth/ui";
import { refThrottled } from "@vueuse/core";
import { Intercom, shutdown } from "@intercom/messenger-js-sdk";
import type { ServerState, Stats, WSEvent, WSInstallationResultEvent } from "~/types/servers";
import { usePyroConsole } from "~/store/console.ts";
const socket = ref<WebSocket | null>(null);
const isReconnecting = ref(false);
Expand Down Expand Up @@ -357,9 +356,8 @@ const serverData = computed(() => server.general);
const error = ref<Error | null>(null);
const isConnected = ref(false);
const isWSAuthIncorrect = ref(false);
const maxConsoleOutput = 5000;
const consoleOutput = ref<string[]>([]);
const throttledConsoleOutput = refThrottled(consoleOutput, 200);
const pyroConsole = usePyroConsole();
console.log("||||||||||||||||||||||| console", pyroConsole.output);
const cpuData = ref<number[]>([]);
const ramData = ref<number[]>([]);
const isActioning = ref(false);
Expand Down Expand Up @@ -439,15 +437,15 @@ const connectWebSocket = () => {
return;
}
consoleOutput.value = [];
pyroConsole.clear();
socket.value?.send(JSON.stringify({ event: "auth", jwt: wsAuth.value?.token }));
isConnected.value = true;
isReconnecting.value = false;
isLoading.value = false;
if (firstConnect.value) {
for (let i = 0; i < initialConsoleMessage.length; i++) {
consoleOutput.value.push(initialConsoleMessage[i]);
pyroConsole.addLine(initialConsoleMessage[i]);
}
}
Expand All @@ -470,9 +468,7 @@ const connectWebSocket = () => {
socket.value.onclose = () => {
if (isMounted.value) {
consoleOutput.value.push(
"\nSomething went wrong with the connection, we're reconnecting...",
);
pyroConsole.addLine("\nSomething went wrong with the connection, we're reconnecting...");
isConnected.value = false;
scheduleReconnect();
}
Expand Down Expand Up @@ -530,10 +526,7 @@ const handleWebSocketMessage = (data: WSEvent) => {
case "log":
// eslint-disable-next-line no-case-declarations
const log = data.message.split("\n").filter((l) => l.trim());
if (consoleOutput.value.length > maxConsoleOutput) {
consoleOutput.value.shift();
}
consoleOutput.value.push(...log);
pyroConsole.addLines(log);
break;
case "stats":
updateStats(data);
Expand Down Expand Up @@ -623,7 +616,7 @@ const onReinstall = (potentialArgs: any) => {
// serverData.value.loader_version = potentialArgs.lVersion;
// serverData.value.mc_version = potentialArgs.mVersion;
// if (potentialArgs?.loader) {
// console.log("setting loader to", potentialArgs.loader);
// console.log("setting loadeconsole
// serverData.value.loader = potentialArgs.loader;
// }
// if (potentialArgs?.lVersion) {
Expand Down
3 changes: 1 addition & 2 deletions apps/frontend/src/pages/servers/manage/[id]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
<UiServersPanelServerStatus :state="serverPowerState" />
</div>
</div>
<UiServersPanelTerminal :console-output="consoleOutput" :full-screen="fullScreen">
<UiServersPanelTerminal :full-screen="fullScreen">
<div class="relative w-full px-4 pt-4">
<ul
v-if="suggestions.length"
Expand Down Expand Up @@ -192,7 +192,6 @@ type ServerProps = {
isConnected: boolean;
isWsAuthIncorrect: boolean;
stats: Stats;
consoleOutput: string[];
serverPowerState: ServerState;
powerStateDetails?: {
oom_killed?: boolean;
Expand Down
68 changes: 68 additions & 0 deletions apps/frontend/src/store/console.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { createGlobalState } from "@vueuse/core";
import { type Ref, ref } from "vue";

/**
* Maximum number of console output lines to store
* @type {number}
*/
const maxLines = 5000;

/**
* Provides a global console output state management system
* Allows adding, storing, and clearing console output with a maximum line limit
*
* @returns {Object} Console state management methods and reactive state
* @property {Ref<string[]>} consoleOutput - Reactive array of console output lines
* @property {function(string): void} addConsoleOutput - Method to add a new console output line
* @property {function(): void} clear - Method to clear all console output
*/
export const usePyroConsole = createGlobalState(() => {
/**
* Reactive array storing console output lines
* @type {Ref<string[]>}
*/
const output: Ref<string[]> = ref<string[]>([]);

/**
* Adds a new output line to the console output
* Automatically removes the oldest line if max output is exceeded
*
* @param {string} line - The console output line to add
*/
const addLine = (line: string): void => {
output.value.push(line);

if (output.value.length > maxLines) {
output.value.shift();
}
};

/**
* Adds multiple output lines to the console output
* Automatically removes the oldest lines if max output is exceeded
*
* @param {string[]} lines - The console output lines to add
* @returns {void}
*/
const addLines = (lines: string[]): void => {
output.value.push(...lines);

if (output.value.length > maxLines) {
output.value.splice(0, output.value.length - maxLines);
}
};

/**
* Clears all console output lines
*/
const clear = (): void => {
output.value = [];
};

return {
output,
addLine,
addLines,
clear,
};
});

0 comments on commit 6ec1dcf

Please sign in to comment.