Optimize shiki and add skipped and left data
This commit is contained in:
parent
2c50459501
commit
f6123f53e7
@ -1,7 +1,14 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<Suspense>
|
||||
<template #default>
|
||||
<RouterView />
|
||||
</template>
|
||||
<template #fallback>
|
||||
<div>Loading</div>
|
||||
</template>
|
||||
</Suspense>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
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,
|
||||
})
|
||||
}, '')
|
||||
|
||||
</script>
|
||||
|
||||
<script></script>
|
||||
@ -175,7 +202,7 @@ getNextSection()
|
||||
Previous
|
||||
</button>
|
||||
<button
|
||||
:disabled="searchResults.current >= searchResults.matches.length"
|
||||
:disabled="searchResults.current >= searchResults.matches.length - 1"
|
||||
@click="moveSearchResult(1)"
|
||||
type="button"
|
||||
>
|
||||
@ -189,8 +216,14 @@ getNextSection()
|
||||
</form>
|
||||
</Header>
|
||||
<main>
|
||||
<div class="code-action">
|
||||
<button @click="getPreviousSection" :disabled="!start">Load Previous</button>
|
||||
<span v-if="start" class="data">{{ dataSkipped }} skipped</span>
|
||||
</div>
|
||||
<div ref="codeDiv" v-html="displayedContent"></div>
|
||||
<div class="code-action">
|
||||
<button @click="getNextSection" :disabled="end >= totalSize">Load More</button>
|
||||
<span v-if="end < totalSize" class="data">{{ dataLeft }} left</span>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
Loading…
x
Reference in New Issue
Block a user