diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 3ed4b37..e3f2c28 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,7 +1,14 @@ - + + + + + + Loading + + diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index 49a605b..c6f66a1 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -68,7 +68,7 @@ header { main { margin-top: 60px; - padding: 30px 30px 0 30px; + padding: 30px; } @media only screen and (max-width: 800px) { @@ -105,3 +105,16 @@ header .logo-text h1 { background-color: #d1d; color: white !important; } + +.code-action { + display: flex; + justify-content: space-between; + align-items: center; +} + +.data { + padding: 10px; + color: var(--primary); + border: none; + border-radius: 10px; +} diff --git a/frontend/src/views/LogView.vue b/frontend/src/views/LogView.vue index 923116b..3e0fba2 100644 --- a/frontend/src/views/LogView.vue +++ b/frontend/src/views/LogView.vue @@ -2,7 +2,10 @@ import { ref, reactive, computed, useTemplateRef, watch, onUpdated } from 'vue' import { useRoute } from 'vue-router' import { computedAsync } from '@vueuse/core' -import { codeToHtml } from 'shiki' +import { createHighlighterCore } from 'shiki/core' +import { createJavaScriptRegexEngine } from 'shiki/engine/javascript' +import catppuccinLatte from '@shikijs/themes/catppuccin-latte' +import bash from '@shikijs/langs/bash' import Header from '../components/Header.vue' @@ -24,33 +27,6 @@ const codeDiv = useTemplateRef('codeDiv') const size = 32 * 1024 const maxViewSize = size * 16 -// Add ellipsis to start or end if there is more content available -const displayedContent = computedAsync(async () => { - const prefix = start.value ? '...\n' : '' - const suffix = end.value < totalSize.value ? '\n...' : '' - const decorations = [] - if (searched) { - const currentMatch = searchResults.matches[searchResults.current] - searchResults.matches.forEach((match) => { - if (match < end.value && match > start.value) { - decorations.push({ - start: match - start.value + prefix.length, - end: match - start.value + query.value.length + prefix.length, - properties: { - class: currentMatch === match ? 'current-highlighted-word' : 'highlighted-word', - }, - }) - } - }) - } - - return await codeToHtml(prefix + content.value + suffix, { - lang: 'bash', - theme: 'catppuccin-latte', - decorations, - }) -}, '') - function scrollToSelectedSearch() { if (!searched.value) return // fragile constants @@ -78,6 +54,7 @@ async function getSection(start: number) { } function getNextSection() { + query.value = "" resetSearch() getSection(end.value).then((data) => { content.value = content.value + data.content @@ -92,6 +69,7 @@ function getNextSection() { } function getPreviousSection() { + query.value = "" resetSearch() if (start.value) { getSection(start.value - size).then((data) => { @@ -144,7 +122,9 @@ function resetSearch() { // +1 for next, -1 for previous function moveSearchResult(n) { - searchResults.current += n + if (searchResults.current + n < searchResults.matches.length && searchResults.current + n >= 0) + searchResults.current += n + else return const matchPos = searchResults.matches[searchResults.current] getNthByte(matchPos) } @@ -158,6 +138,53 @@ function findError() { } getNextSection() + +function convertBytes(bytes) { + const suffixes = ["bytes", "KiB", "MiB", "GiB"]; + for (let suffix of suffixes) { + if (bytes < 1024) + return `${bytes.toFixed(2).replace(/\.?0*$/,'')} ${suffix}` + bytes /= 1024; + } + return `${bytes.toFixed(2).replace(/\.?0*$/,'')} TiB` +} + +const dataSkipped = computed(() => convertBytes(start.value)) +const dataLeft = computed(() => convertBytes(totalSize.value - end.value)) + +const highlighter = await createHighlighterCore({ + langs: [bash], + themes: [catppuccinLatte], + engine: createJavaScriptRegexEngine(), +}); + +// Add ellipsis to start or end if there is more content available +const displayedContent = computed(() => { + const prefix = start.value ? '...\n' : '' + const suffix = end.value < totalSize.value ? '\n...' : '' + const decorations = [] + if (searched) { + const currentMatch = searchResults.matches[searchResults.current] + searchResults.matches.forEach((match) => { + if (match < end.value && match > start.value) { + decorations.push({ + start: match - start.value + prefix.length, + end: match - start.value + query.value.length + prefix.length, + properties: { + class: currentMatch === match ? 'current-highlighted-word' : 'highlighted-word', + }, + }) + } + }) + } + + return highlighter.codeToHtml(prefix + content.value + suffix, { + lang: 'bash', + theme: 'catppuccin-latte', + decorations, + }) +}, '') + @@ -175,7 +202,7 @@ getNextSection() Previous @@ -189,8 +216,14 @@ getNextSection() - Load Previous + + Load Previous + {{ dataSkipped }} skipped + - Load More + + Load More + {{ dataLeft }} left +