WIP read-only view
This commit is contained in:
+11
-11
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<title>Paste</title>
|
||||
<meta charset="utf-8" />
|
||||
@@ -9,23 +9,23 @@
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="https://cdn.jsdelivr.net/combine/
|
||||
npm/codemirror@5.65.5/lib/codemirror.min.css,
|
||||
npm/codemirror@5.65.5/theme/dracula.min.css"
|
||||
npm/codemirror@5.65.16/lib/codemirror.min.css,
|
||||
npm/codemirror@5.65.16/theme/nord.min.css"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents" class="h-screen text-gray-300">%sveltekit.body%</div>
|
||||
<div class="dark:text-gray-300 dark:bg-gray-800 min-h-screen">%sveltekit.body%</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/combine/
|
||||
npm/lzma@2.3.2/src/lzma.min.js,
|
||||
npm/codemirror@5.65.5,
|
||||
npm/codemirror@5.65.5/addon/mode/loadmode.min.js,
|
||||
npm/codemirror@5.65.5/addon/mode/overlay.min.js,
|
||||
npm/codemirror@5.65.5/addon/mode/multiplex.min.js,
|
||||
npm/codemirror@5.65.5/addon/mode/simple.min.js,
|
||||
npm/codemirror@5.65.5/addon/scroll/simplescrollbars.js,
|
||||
npm/codemirror@5.65.5/mode/meta.min.js"></script>
|
||||
npm/codemirror@5.65.16,
|
||||
npm/codemirror@5.65.16/addon/mode/loadmode.min.js,
|
||||
npm/codemirror@5.65.16/addon/mode/overlay.min.js,
|
||||
npm/codemirror@5.65.16/addon/mode/multiplex.min.js,
|
||||
npm/codemirror@5.65.16/addon/mode/simple.min.js,
|
||||
npm/codemirror@5.65.16/addon/scroll/simplescrollbars.js,
|
||||
npm/codemirror@5.65.16/mode/meta.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
})
|
||||
|
||||
export function setLanguage(lang: string) {
|
||||
if (lang === 'mrwn') { // back compatiblity with old links
|
||||
lang = 'md'
|
||||
}
|
||||
const language = languages.find((e) => e.value === lang)!
|
||||
selectedLanguage = language
|
||||
// Automatic text wrap for plain text or markdown
|
||||
@@ -71,7 +74,9 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-wrap justify-between items-center px-3 py-1 text-sm relative z-10 shadow-md gap-2">
|
||||
<div
|
||||
class="flex flex-wrap justify-between items-center px-3 py-1 text-sm relative z-10 shadow-md gap-2"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<h1 class="text-xl">Paste</h1>
|
||||
<span class="ml-8 text-xs">
|
||||
@@ -93,8 +98,8 @@
|
||||
class="border border-gray-300 bg-transparent p-1 grow"
|
||||
value={$shareUrl}
|
||||
/>
|
||||
<button on:click={copyUrl}> Copy </button>
|
||||
<button on:click={closeUrlInput}> Close </button>
|
||||
<button on:click={copyUrl}>Copy</button>
|
||||
<button on:click={closeUrlInput}>Close</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex justify-end gap-2">
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ export const slugify = (str: string) =>
|
||||
.replace(/[^\w\-]+/g, '')
|
||||
|
||||
export const shorten = (name: string) => {
|
||||
let n = slugify(name).replace('script', '-s').replace('python', 'py')
|
||||
let n = slugify(name).replace('script', '-s').replace('python', 'py').replace('markdown', 'md')
|
||||
const nov = (s: string) => s[0] + s.substr(1).replace(/[aeiouy-]/g, '')
|
||||
if (n.replace(/-/g, '').length <= 4) {
|
||||
return n.replace(/-/g, '')
|
||||
|
||||
+50
-88
@@ -1,115 +1,77 @@
|
||||
<script lang="ts">
|
||||
import type { Editor } from 'codemirror'
|
||||
import { onMount } from 'svelte'
|
||||
import { debounce } from 'lodash-es'
|
||||
import * as brotli from '$lib/brotli'
|
||||
import * as lzma from '$lib/lzma'
|
||||
import { byId } from '$lib/utils'
|
||||
import TopBar from '../components/TopBar.svelte'
|
||||
import { selectedLang, shareUrl } from '../store'
|
||||
import { unified } from 'unified'
|
||||
import remarkParse from 'remark-parse'
|
||||
import remarkRehype from 'remark-rehype'
|
||||
import rehypeHighlight from 'rehype-highlight'
|
||||
import rehypeStringify from 'rehype-stringify'
|
||||
import hljs from 'highlight.js'
|
||||
import 'highlight.js/styles/nord.min.css'
|
||||
|
||||
let editor: Editor | null = null
|
||||
const readOnly = false
|
||||
|
||||
let charLen = 0
|
||||
let compressed: string = ''
|
||||
let waiting = false
|
||||
let setLanguage: (lang: string) => void
|
||||
let decompressed: string
|
||||
let isMarkdown = false
|
||||
let isPlainText = false
|
||||
|
||||
onMount(async () => {
|
||||
initCodeEditor()
|
||||
let lang = new URLSearchParams(window.location.search).get('l')
|
||||
lang = lang === 'mrwn' ? 'md' : lang // back compatiblity with old links
|
||||
|
||||
// extract the part in the url after the hash
|
||||
const hash = window.location.hash.slice(1)
|
||||
if (hash) {
|
||||
// decompress the data
|
||||
let decompressed: string
|
||||
if (hash.startsWith('XQAAA')) {
|
||||
decompressed = await lzma.decompress(hash)
|
||||
} else {
|
||||
decompressed = await brotli.decompress(hash)
|
||||
}
|
||||
// set the editor value
|
||||
if (editor) {
|
||||
editor.setValue(decompressed)
|
||||
}
|
||||
}
|
||||
|
||||
const lang = new URLSearchParams(window.location.search).get('l')
|
||||
if (lang) {
|
||||
setLanguage(lang)
|
||||
}
|
||||
else {
|
||||
setLanguage('plt')
|
||||
// Markdown
|
||||
if (lang === 'md' || lang === 'gflm') {
|
||||
const html = await unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkRehype)
|
||||
.use(rehypeHighlight)
|
||||
.use(rehypeStringify)
|
||||
.process(decompressed)
|
||||
isMarkdown = true
|
||||
decompressed = html.toString()
|
||||
}
|
||||
// Plain text
|
||||
else if (lang === 'plt' || !lang) {
|
||||
isPlainText = true
|
||||
}
|
||||
// Code
|
||||
else {
|
||||
decompressed = hljs.highlight(lang, decompressed).value
|
||||
}
|
||||
} else {
|
||||
// Redirect to editor page
|
||||
window.location.href = '/editor'
|
||||
}
|
||||
})
|
||||
|
||||
async function updateShareUrl() {
|
||||
if (editor) {
|
||||
const url = new URL(window.location.href)
|
||||
compressed = await brotli.compress(editor.getValue())
|
||||
if ($selectedLang) {
|
||||
url.searchParams.set('l', $selectedLang)
|
||||
}
|
||||
url.hash = compressed
|
||||
$shareUrl = url.toString()
|
||||
}
|
||||
}
|
||||
|
||||
const updateShareUrlDebounced = debounce(async () => {
|
||||
updateShareUrl()
|
||||
waiting = false
|
||||
}, 1000)
|
||||
|
||||
const initCodeEditor = () => {
|
||||
CodeMirror.modeURL = 'https://cdn.jsdelivr.net/npm/codemirror@5.65.5/mode/%N/%N.js'
|
||||
editor = new CodeMirror(byId('editor'), {
|
||||
lineNumbers: true,
|
||||
theme: 'dracula',
|
||||
readOnly: readOnly,
|
||||
lineWrapping: false,
|
||||
scrollbarStyle: 'native',
|
||||
}) as Editor
|
||||
|
||||
if (readOnly) {
|
||||
document.body.classList.add('readonly')
|
||||
}
|
||||
|
||||
editor.on('change', async () => {
|
||||
if (editor) {
|
||||
waiting = true
|
||||
charLen = editor.getValue().length
|
||||
updateShareUrlDebounced()
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col font-mono h-screen bg-gray-700">
|
||||
<TopBar {editor} {updateShareUrl} bind:setLanguage />
|
||||
<div>
|
||||
|
||||
<div id="editor" class="grow overflow-hidden" />
|
||||
|
||||
{editor?.getValue()}
|
||||
|
||||
<div id="footer" class="p-2 text-sm z-10">
|
||||
Data length: {charLen}
|
||||
| Link length: {waiting ? '?' : $shareUrl.length}
|
||||
({waiting || !compressed.length || !charLen
|
||||
? '?'
|
||||
: Math.round(($shareUrl.length / charLen) * 100)}% of original)
|
||||
<div class="prose dark:prose-invert lg:py-12 p-[0.5em] md:max-w-3xl md:mx-auto lg:max-w-4xl">
|
||||
{#if isMarkdown}
|
||||
{@html decompressed}
|
||||
{:else if isPlainText}
|
||||
<div class="whitespace-pre-line">
|
||||
{decompressed}
|
||||
</div>
|
||||
{:else}
|
||||
<pre><code>{@html decompressed}</code></pre>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(div.CodeMirror) {
|
||||
height: 100%;
|
||||
font-size: 15px;
|
||||
}
|
||||
#footer {
|
||||
--tw-shadow: 0 -4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 -4px 6px -1px var(--tw-shadow-color),
|
||||
0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
<style lang="scss">
|
||||
:global(pre code.hljs) {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
|
||||
<script lang="ts">
|
||||
import type { Editor } from 'codemirror'
|
||||
import { onMount } from 'svelte'
|
||||
import { debounce } from 'lodash-es'
|
||||
import * as brotli from '$lib/brotli'
|
||||
import * as lzma from '$lib/lzma'
|
||||
import { byId } from '$lib/utils'
|
||||
import TopBar from '../../components/TopBar.svelte'
|
||||
import { selectedLang, shareUrl } from '../../store'
|
||||
|
||||
let editor: Editor | null = null
|
||||
const readOnly = false
|
||||
|
||||
let charLen = 0
|
||||
let compressed: string = ''
|
||||
let waiting = false
|
||||
let setLanguage: (lang: string) => void
|
||||
|
||||
onMount(async () => {
|
||||
initCodeEditor()
|
||||
// extract the part in the url after the hash
|
||||
const hash = window.location.hash.slice(1)
|
||||
if (hash) {
|
||||
// decompress the data
|
||||
let decompressed: string
|
||||
if (hash.startsWith('XQAAA')) {
|
||||
decompressed = await lzma.decompress(hash)
|
||||
} else {
|
||||
decompressed = await brotli.decompress(hash)
|
||||
}
|
||||
// set the editor value
|
||||
if (editor) {
|
||||
editor.setValue(decompressed)
|
||||
}
|
||||
}
|
||||
|
||||
const lang = new URLSearchParams(window.location.search).get('l')
|
||||
if (lang) {
|
||||
setLanguage(lang)
|
||||
} else {
|
||||
setLanguage('plt')
|
||||
}
|
||||
})
|
||||
|
||||
async function updateShareUrl() {
|
||||
if (editor) {
|
||||
const url = new URL(window.location.origin)
|
||||
compressed = await brotli.compress(editor.getValue())
|
||||
if ($selectedLang) {
|
||||
url.searchParams.set('l', $selectedLang)
|
||||
}
|
||||
url.hash = compressed
|
||||
$shareUrl = url.toString()
|
||||
}
|
||||
}
|
||||
|
||||
const updateShareUrlDebounced = debounce(async () => {
|
||||
updateShareUrl()
|
||||
waiting = false
|
||||
}, 1000)
|
||||
|
||||
const initCodeEditor = () => {
|
||||
CodeMirror.modeURL = 'https://cdn.jsdelivr.net/npm/codemirror@5.65.16/mode/%N/%N.js'
|
||||
editor = new CodeMirror(byId('editor'), {
|
||||
lineNumbers: true,
|
||||
theme: 'nord',
|
||||
readOnly: readOnly,
|
||||
lineWrapping: false,
|
||||
scrollbarStyle: 'native',
|
||||
}) as Editor
|
||||
|
||||
if (readOnly) {
|
||||
document.body.classList.add('readonly')
|
||||
}
|
||||
|
||||
editor.on('change', async () => {
|
||||
if (editor) {
|
||||
waiting = true
|
||||
charLen = editor.getValue().length
|
||||
updateShareUrlDebounced()
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col font-mono h-screen bg-gray-700">
|
||||
<TopBar {editor} {updateShareUrl} bind:setLanguage />
|
||||
|
||||
<div id="editor" class="grow overflow-hidden" />
|
||||
|
||||
{editor?.getValue()}
|
||||
|
||||
<div id="footer" class="p-2 text-sm z-10">
|
||||
Data length: {charLen}
|
||||
| Link length: {waiting ? '?' : $shareUrl.length}
|
||||
({waiting || !compressed.length || !charLen
|
||||
? '?'
|
||||
: Math.round(($shareUrl.length / charLen) * 100)}% of original)
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(div.CodeMirror) {
|
||||
height: 100%;
|
||||
font-size: 15px;
|
||||
}
|
||||
#footer {
|
||||
--tw-shadow: 0 -4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 -4px 6px -1px var(--tw-shadow-color),
|
||||
0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user