Skip to content

Commit

Permalink
feat: Code highlighting
Browse files Browse the repository at this point in the history
Fixes #30
  • Loading branch information
arildm committed Jun 11, 2024
1 parent 54549cc commit 923cc13
Show file tree
Hide file tree
Showing 15 changed files with 125 additions and 28 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ As this project is a user-facing application, the places in the semantic version

## [Unreleased]

### Added

- Code highlighting for XML and YAML [#30](https://github.com/spraakbanken/mink-frontend/issues/30)

### Changed

- Drop unnecessary async usage of the `js-yaml` package
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/vue-fontawesome": "^3.0.1",
"@highlightjs/vue-plugin": "^2.1.0",
"@modyfi/vite-plugin-yaml": "^1.0.4",
"@vitejs/plugin-vue": "^5",
"@vue/tsconfig": "^0.5.1",
Expand All @@ -31,6 +32,7 @@
"datatransfer-files-promise": "^2.0.0",
"eslint": "^8.15.0",
"filesize": "^10.0.6",
"highlight.js": "^11",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"pinia": "^2.0.28",
Expand Down
12 changes: 12 additions & 0 deletions src/components/HeightResizable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script setup lang="ts"></script>

<template>
<div class="overflow-scroll h-fit max-h-60 resize-y"><slot /></div>
</template>

<style scoped>
/* Override max-height when resized. */
.resize-y[style*="height"] {
max-height: unset;
}
</style>
21 changes: 21 additions & 0 deletions src/components/SyntaxHighlight.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
defineProps<{
code: string;
language?: string;
}>();
</script>

<template>
<highlightjs
:code
:language
:autodetect="false"
class="text-xs rounded whitespace-pre-wrap [overflow-wrap:anywhere]"
/>
</template>

<style>
pre code.hljs {
@apply p-2 bg-stone-800;
}
</style>
2 changes: 1 addition & 1 deletion src/components/TerminalOutput.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<pre
class="bg-stone-600 text-lime-50 text-xs p-2 rounded whitespace-pre-wrap [overflow-wrap:anywhere]"
class="bg-stone-800 text-lime-50 text-xs p-2 rounded whitespace-pre-wrap [overflow-wrap:anywhere]"
><slot /></pre>
</template>
11 changes: 8 additions & 3 deletions src/components/TextData.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
<script setup lang="ts">
import HeightResizable from "@/components/HeightResizable.vue";
import TerminalOutput from "@/components/TerminalOutput.vue";
import SyntaxHighlight from "@/components/SyntaxHighlight.vue";
import type { SyntaxLanguage } from "@/highlight";
defineProps<{
text: string;
language?: SyntaxLanguage;
}>();
</script>

<template>
<TerminalOutput class="h-fit max-h-40 resize-y">
{{ text }}
</TerminalOutput>
<HeightResizable>
<SyntaxHighlight v-if="language" :code="text" :language />
<TerminalOutput v-else>{{ text }}</TerminalOutput>
</HeightResizable>
</template>

<style scoped>
Expand Down
10 changes: 5 additions & 5 deletions src/corpus/config/CorpusConfigCustom.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script setup lang="ts">
import type { AxiosError } from "axios";
import useCorpusIdParam from "../corpusIdParam.composable";
import useConfig from "./config.composable";
import useCorpusIdParam from "@/corpus/corpusIdParam.composable";
import useConfig from "@/corpus/config/config.composable";
import { useAuth } from "@/auth/auth.composable";
import FileUpload from "@/components/FileUpload.vue";
import HelpBox from "@/components/HelpBox.vue";
import LayoutBox from "@/components/LayoutBox.vue";
import TerminalOutput from "@/components/TerminalOutput.vue";
import type { MinkResponse } from "@/api/api.types";
import useMessenger from "@/message/messenger.composable";
import { useAuth } from "@/auth/auth.composable";
import SyntaxHighlight from "@/components/SyntaxHighlight.vue";
const corpusId = useCorpusIdParam();
const { config, uploadConfigRaw } = useConfig(corpusId);
Expand Down Expand Up @@ -46,7 +46,7 @@ async function upload(files: File[]) {

<div class="flex flex-wrap gap-4">
<LayoutBox class="w-96 grow" :title="$t('show')">
<TerminalOutput v-if="config">{{ config }}</TerminalOutput>
<SyntaxHighlight v-if="config" language="yaml" :code="config" />
</LayoutBox>

<LayoutBox class="w-96 grow" :title="$t('upload')">
Expand Down
10 changes: 8 additions & 2 deletions src/corpus/config/CorpusConfiguration.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import useSources from "@/corpus/sources/sources.composable";
import useConfig from "@/corpus/config/config.composable";
import type { ByLang } from "@/util.types";
import LayoutBox from "@/components/LayoutBox.vue";
import TerminalOutput from "@/components/TerminalOutput.vue";
const router = useRouter();
const corpusId = useCorpusIdParam();
Expand Down Expand Up @@ -171,8 +172,13 @@ async function submit(fields: Form) {
disabled
:value="corpusId"
:help="$t('metadata.identifier.help')"
input-class="font-mono bg-stone-600 text-lime-50 text-xs p-2 rounded"
/>
>
<template #input>
<TerminalOutput class="inline leading-loose">
{{ corpusId }}
</TerminalOutput>
</template>
</FormKit>
</LayoutSection>

<LayoutSection :title="$t('configuration')">
Expand Down
13 changes: 4 additions & 9 deletions src/corpus/job/JobStatus.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useCorpusState } from "@/corpus/corpusState.composable";
import ActionButton from "@/components/ActionButton.vue";
import TerminalOutput from "@/components/TerminalOutput.vue";

Check warning on line 10 in src/corpus/job/JobStatus.vue

View workflow job for this annotation

GitHub Actions / build (18.x)

'TerminalOutput' is defined but never used

Check warning on line 10 in src/corpus/job/JobStatus.vue

View workflow job for this annotation

GitHub Actions / build (20.x)

'TerminalOutput' is defined but never used

Check warning on line 10 in src/corpus/job/JobStatus.vue

View workflow job for this annotation

GitHub Actions / build (22.x)

'TerminalOutput' is defined but never used
import ProgressBar from "@/components/ProgressBar.vue";
import TextData from "@/components/TextData.vue";
const corpusId = useCorpusIdParam();
const { abortJob, jobStatus, isJobRunning } = useJob(corpusId);
Expand Down Expand Up @@ -58,9 +59,7 @@ const hasStarted = computed(
</tr>
<tr v-if="jobStatus.errors">
<td colspan="2">
<TerminalOutput class="mb-2 h-fit max-h-20 resize-y">
{{ jobStatus.errors }}
</TerminalOutput>
<TextData :text="jobStatus.errors" class="mb-2" />
</td>
</tr>

Expand All @@ -69,9 +68,7 @@ const hasStarted = computed(
</tr>
<tr v-if="jobStatus.warnings">
<td colspan="2">
<TerminalOutput class="mb-2 h-fit max-h-20 resize-y">
{{ jobStatus.warnings }}
</TerminalOutput>
<TextData :text="jobStatus.warnings" class="mb-2" />
</td>
</tr>

Expand All @@ -80,9 +77,7 @@ const hasStarted = computed(
</tr>
<tr v-if="isFailed && jobStatus.sparv_output">
<td colspan="2">
<TerminalOutput class="mb-2 h-fit max-h-20 resize-y">
{{ jobStatus.sparv_output }}
</TerminalOutput>
<TextData :text="jobStatus.sparv_output" class="mb-2" />
</td>
</tr>

Expand Down
4 changes: 3 additions & 1 deletion src/corpus/sources/SourceText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import once from "lodash/once";
import { downloadFile } from "@/util";
import ActionButton from "@/components/ActionButton.vue";
import TextData from "@/components/TextData.vue";
import type { SyntaxLanguage } from "@/highlight";
/** Defer loading if file is large. */
const AUTOLOAD_LIMIT = 500_000;
Expand All @@ -13,6 +14,7 @@ const props = defineProps<{
filename: string;
size?: number;
noLoad?: boolean;
language?: SyntaxLanguage & "xml";
}>();
const text = ref();
Expand All @@ -38,7 +40,7 @@ async function download() {
</script>

<template>
<TextData v-if="text" :text="text" />
<TextData v-if="text" :text="text" :language="language" />

<div class="my-2 flex gap-2">
<ActionButton v-if="!noLoad && text === undefined" @click="show()">
Expand Down
2 changes: 2 additions & 0 deletions src/corpus/sources/SourceView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const metadata = computed(() =>
);
const isBinary = computed(() => metadata.value?.type.indexOf("text/") !== 0);
const isPlaintext = computed(() => metadata.value?.type == "text/plain");
const isXml = computed(() => /\/xml$/.test(metadata.value?.type || ""));
async function loadRaw() {
return (
Expand Down Expand Up @@ -67,6 +68,7 @@ async function loadPlain() {
:filename="metadata.name"
:no-load="isBinary"
:size="metadata.size"
:language="isXml ? 'xml' : undefined"
/>
</PendingContent>
</td>
Expand Down
12 changes: 12 additions & 0 deletions src/highlight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import hljs from "highlight.js/lib/core";
import xml from "highlight.js/lib/languages/xml";
import yaml from "highlight.js/lib/languages/yaml";
import highlightPlugin from "@highlightjs/vue-plugin";
import "highlight.js/styles/base16/monokai.css";

hljs.registerLanguage("xml", xml);
hljs.registerLanguage("yaml", yaml);

export default highlightPlugin;

export type SyntaxLanguage = "xml" | "yaml";
7 changes: 3 additions & 4 deletions src/library/AdminResourcePreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { type Resource, isCorpus } from "@/store/resource.store";
import TerminalOutput from "@/components/TerminalOutput.vue";

Check warning on line 4 in src/library/AdminResourcePreview.vue

View workflow job for this annotation

GitHub Actions / build (18.x)

'TerminalOutput' is defined but never used

Check warning on line 4 in src/library/AdminResourcePreview.vue

View workflow job for this annotation

GitHub Actions / build (20.x)

'TerminalOutput' is defined but never used

Check warning on line 4 in src/library/AdminResourcePreview.vue

View workflow job for this annotation

GitHub Actions / build (22.x)

'TerminalOutput' is defined but never used
import useConfig from "@/corpus/config/config.composable";
import PendingContent from "@/spin/PendingContent.vue";
import TextData from "@/components/TextData.vue";
const props = defineProps<{
resourceId: string;
Expand Down Expand Up @@ -59,14 +60,12 @@ onMounted(() => {
</tbody>
</table>
<PendingContent
v-if="isCorpus(resource)"
v-if="isCorpus(resource) && resource.config"
:on="`corpus/${resourceId}/config`"
class="flex-1"
>
<h3 class="font-bold">{{ $t("configuration") }}</h3>
<TerminalOutput class="max-h-80">
{{ resource.config }}
</TerminalOutput>
<TextData :text="resource.config" language="yaml" />
</PendingContent>
</div>
</template>
Expand Down
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import router from "@/router/router";
import App from "@/App.vue";
import i18n from "@/i18n/i18n";
import { formkit, formkitConfig } from "@/formkit";
import highlight from "@/highlight";
import { FontAwesomeIcon } from "@/fontawesome";
import "@/index.css";

Expand All @@ -15,6 +16,7 @@ const app = createApp(App) //
.use(pinia)
.use(i18n)
.use(formkit, formkitConfig)
.use(highlight)
.component("icon", FontAwesomeIcon);

// Use the Matomo plugin only if configured in env.
Expand Down
41 changes: 38 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,11 @@
resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.6.tgz#c5e627475c10869a091280610c96411d18206f3a"
integrity sha512-akrL7lTroyNpPkoHtvK2UpsMzJr6jXdHaQ0YdcwqDsB8jdwlpNHZYijpOUd9KJsARr+VB3WXY4EyObepqJ4ytQ==

"@highlightjs/vue-plugin@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz#b7c41e3597a46975665b10cad57882cbde1d1594"
integrity sha512-E+bmk4ncca+hBEYRV2a+1aIzIV0VSY/e5ArjpuSN9IO7wBJrzUE2u4ESCwrbQD7sAy+jWQjkV5qCCWgc+pu7CQ==

"@humanwhocodes/config-array@^0.11.14":
version "0.11.14"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
Expand Down Expand Up @@ -2236,6 +2241,11 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==

highlight.js@^11:
version "11.9.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.9.0.tgz#04ab9ee43b52a41a047432c8103e2158a1b8b5b0"
integrity sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==

human-signals@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
Expand Down Expand Up @@ -3374,7 +3384,16 @@ stop-iteration-iterator@^1.0.0:
dependencies:
internal-slot "^1.0.4"

"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -3420,7 +3439,14 @@ string.prototype.trimstart@^1.0.7:
define-properties "^1.2.0"
es-abstract "^1.22.1"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down Expand Up @@ -3872,7 +3898,16 @@ why-is-node-running@^2.2.2:
siginfo "^2.0.0"
stackback "0.0.2"

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand Down

0 comments on commit 923cc13

Please sign in to comment.