Optimize shiki and add skipped and left data
This commit is contained in:
		@@ -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>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user