upend/webui/src/components/display/blobs/TextViewer.svelte

118 lines
2.5 KiB
Svelte

<script lang="ts">
import api from "../../../lib/api";
import IconButton from "../../utils/IconButton.svelte";
import Spinner from "../../utils/Spinner.svelte";
export let address: string;
let mode: "preview" | "full" | "markdown" = "preview";
$: textContent = (async () => {
const response = await api.fetchRaw(address, mode == "preview");
const text = await response.text();
if (mode === "markdown") {
const { marked } = await import("marked");
const DOMPurify = await import("dompurify");
return DOMPurify.default.sanitize(marked.parse(text));
} else {
return text;
}
})();
const tabs = [
["image", "preview", "Preview"],
["shape-circle", "full", "Full"],
["edit", "markdown", "Markdown"],
] as [string, typeof mode, string][];
</script>
<div class="text-preview">
<header class="text-header">
{#each tabs as [icon, targetMode, label]}
<div
class="tab"
class:active={mode == targetMode}
on:click={() => (mode = targetMode)}
on:keydown={(ev) => {
if (ev.key === "Enter") {
mode = targetMode;
}
}}
>
<IconButton
name={icon}
active={mode == targetMode}
on:click={() => (mode = targetMode)}
/>
<div class="label">{label}</div>
</div>
{/each}
</header>
<div class="text" class:markdown={mode === "markdown"}>
{#await textContent}
<Spinner centered />
{:then text}
{#if mode === "markdown"}
{@html text}
{:else}
{text}{#if mode === "preview"}{/if}
{/if}
{/await}
</div>
</div>
<style lang="scss">
.text-preview {
flex: 1;
min-width: 0;
}
.text {
background: var(--background);
padding: 0.5em;
height: 100%;
box-sizing: border-box;
overflow: auto;
border-radius: 4px;
border: 1px solid var(--foreground);
white-space: pre-wrap;
&.markdown {
white-space: unset;
:global(img) {
max-width: 75%;
}
}
}
header {
display: flex;
justify-content: flex-end;
.tab {
display: flex;
cursor: pointer;
border: 1px solid var(--foreground);
border-bottom: 0;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
padding: 0.15em;
margin: 0 0.1em;
&.active {
background: var(--background);
}
.label {
margin-right: 0.5em;
}
}
}
</style>