Compare commits
24
Commits
e1b7961f45
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
819d9cb77d | ||
|
|
5b685e251b | ||
|
|
0d61c7662a | ||
|
|
ed2fd9f45e | ||
|
|
7f8091120c | ||
|
|
05adc4574a | ||
|
|
0d14e6a6d8 | ||
|
|
b4ae405876 | ||
|
|
1c174a3b1c | ||
|
|
c3e2bbe4c0 | ||
|
|
ac7b41ecfc | ||
|
|
52c1f80d4b | ||
|
|
cd899df541 | ||
|
|
4184484825 | ||
|
|
b700ef73ad | ||
|
|
0598e0c6bd | ||
|
|
30e4d5c87e | ||
|
|
a55a5f6b06 | ||
|
|
291eb3dbce | ||
|
|
ac53680831 | ||
|
|
3b0918311b | ||
|
|
1afb6b664f | ||
|
|
74730206bf | ||
|
|
073c6e402e |
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
|
"bracketSameLine": true,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
|
|||||||
@@ -4,7 +4,33 @@
|
|||||||
|
|
||||||
This is a rewrite of https://nopaste.boris.sh/, which is a rewrite of https://topaz.github.io/paste/
|
This is a rewrite of https://nopaste.boris.sh/, which is a rewrite of https://topaz.github.io/paste/
|
||||||
|
|
||||||
[More information on this self-hosted "about" page](https://paste.scambier.xyz/?l=md#G0oEIKySx5UfTkdu1XoEsNFTBka+1+MnKOMEeLUaweWLc/oYlbttJz93QOgIkhdODeUmDs/+u3T5RANkM7+ktGLRxbTN0WIKyb2AWJbsnIoNDvzMtbcgQpsQYPWXPwUeRkuMWOXIEYIfzvnjqY85WNtT9Rjq3Y4/qHB1+mjzCQsTvr9UIfLbq28+i4joe5i/F2SQCTO9G/SlgnojZOKr9BSbIq0EPVBnVgg9T8+zls2Ezgg67tZTjX8pVB70RIghiwfFeqn/ZMQv9VGcbEWDIHnUlL52CL4FeCQ6DMJ4OOtVWk9jWI3i1CRvzxbmHudPlk1iA4pD9wHgOOILFPKQUAntulosi6VRhJ9eeSmViKpbloonQX9cDQVUkg8rzdkoBllTTfnVcYwDRsMS3UjTv/I+befAQeNMiRc7ESFegwUd6yEFPyVJG8RCXcgvw1QfdBe93uXQ9qGBL6BhLpjlpqxjmf9fwkKHFYClXmn7yaZrc/HrW6YH5F6Ei9MGKMvY5be++/noh6TJ5MeGgDzeJgNr0rjvcltZrDRS8wumbWpseMjTY1bz/rp/fJVQxwy/Rd+kXjlGrMJLZsVVwED5b0Jr26wUAm85shUyLX50kklq6ZIRfnzNX4LWrnl72vWR1YaTr/3n+Cp+6IBEliYA)
|
[More information on this self-hosted "about" page](https://paste.scambier.xyz/about)
|
||||||
|
|
||||||
|
## Maximum lengths for links
|
||||||
|
|
||||||
|
NoPaste is great for sharing code snippets on various platforms.
|
||||||
|
|
||||||
|
These are the maximum link lengths on some apps and browsers.
|
||||||
|
|
||||||
|
| App | Max length | Notes |
|
||||||
|
| -------- | ---------- | --------------------------------------------------------------------------------- |
|
||||||
|
| Mastodon | None? | Not specified in the docs |
|
||||||
|
| Reddit | 10,000 | |
|
||||||
|
| Twitter | 4,088 | |
|
||||||
|
| Slack | 4,000 | |
|
||||||
|
| Discord | 4,000 | 4,000 is the whole message limit. Longer messages are converted into attachements |
|
||||||
|
| QR Code | 2,610 | |
|
||||||
|
| Bitly | 2,048 | |
|
||||||
|
|
||||||
|
| Browser | Max length | Notes |
|
||||||
|
| --------------- | ------------------------- | --------------------------------------- |
|
||||||
|
| Google Chrome | (win) 32,779 (mac) 10,000 | Will not display, but larger links work |
|
||||||
|
| Firefox | >64,000 | |
|
||||||
|
| Microsoft Edge | 2,083 | Anything over 2083 will fail |
|
||||||
|
| Android | 8,192 | |
|
||||||
|
| Safari | Lots | |
|
||||||
|
|
||||||
|
The safest limit to make sure your Paste link works everywhere seems to be 2,083.
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
|||||||
+31
-31
@@ -12,44 +12,44 @@
|
|||||||
"format": "prettier --plugin-search-dir . --write ."
|
"format": "prettier --plugin-search-dir . --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^2.0.0",
|
"@sveltejs/adapter-auto": "^2.1.1",
|
||||||
"@sveltejs/kit": "^1.20.4",
|
"@sveltejs/kit": "^1.30.4",
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
"@types/codemirror": "^5.60.12",
|
"@types/codemirror": "^5.60.16",
|
||||||
"@types/lodash-es": "^4.17.10",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.21",
|
||||||
"eslint": "^8.28.0",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.10.2",
|
||||||
"eslint-plugin-svelte": "^2.30.0",
|
"eslint-plugin-svelte": "^2.46.1",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.5.6",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.8",
|
||||||
"prettier-plugin-svelte": "^2.10.1",
|
"prettier-plugin-svelte": "^2.10.1",
|
||||||
"svelte": "^4.0.5",
|
"svelte": "^4.2.20",
|
||||||
"svelte-check": "^3.4.3",
|
"svelte-check": "^3.8.6",
|
||||||
"tailwindcss": "^3.3.5",
|
"tailwindcss": "^3.4.17",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.9.2",
|
||||||
"vite": "^4.4.2"
|
"vite": "^4.5.14"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/svelte": "^3.1.4",
|
"@iconify/svelte": "^3.1.6",
|
||||||
"@svelte-put/clickoutside": "^3.0.0",
|
"@svelte-put/clickoutside": "^3.0.2",
|
||||||
"@sveltejs/adapter-static": "^2.0.3",
|
"@sveltejs/adapter-static": "^2.0.3",
|
||||||
"brotli-compress": "^1.3.3",
|
"brotli-compress": "^1.3.3",
|
||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.11.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"rehype-highlight": "^7.0.0",
|
"rehype-highlight": "^7.0.2",
|
||||||
"rehype-stringify": "^10.0.0",
|
"rehype-stringify": "^10.0.1",
|
||||||
"remark-gfm": "^4.0.0",
|
"remark-gfm": "^4.0.1",
|
||||||
"remark-parse": "^11.0.0",
|
"remark-parse": "^11.0.0",
|
||||||
"remark-rehype": "^11.0.0",
|
"remark-rehype": "^11.1.2",
|
||||||
"sass": "^1.69.5",
|
"sass": "^1.91.0",
|
||||||
"slim-select": "^2.6.0",
|
"slim-select": "^2.12.1",
|
||||||
"svelte-select": "^5.7.0",
|
"svelte-select": "^5.8.3",
|
||||||
"tw-elements": "^1.0.0",
|
"tw-elements": "^1.1.0",
|
||||||
"unified": "^11.0.4"
|
"unified": "^11.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+2895
-2126
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
@@ -9,4 +9,4 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {};
|
export {}
|
||||||
|
|||||||
+5
-9
@@ -4,19 +4,15 @@
|
|||||||
<title>Paste</title>
|
<title>Paste</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
|
||||||
<link rel="preload" as="font" type="text/css" href="%sveltekit.assets%/hack-subset.css" />
|
<link rel="stylesheet" href="%sveltekit.assets%/hack-subset.css" />
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css"
|
|
||||||
href="https://cdn.jsdelivr.net/combine/
|
|
||||||
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" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta property="og:title" content="Paste - No-database paste service" />
|
<meta property="og:title" content="Paste - No-database paste service" />
|
||||||
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/bokub/nopaste@images/logo.png" />
|
|
||||||
<meta property="og:url" content="https://paste.scambier.xyz" />
|
<meta property="og:url" content="https://paste.scambier.xyz" />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
property="og:description"
|
||||||
|
content="Paste is a client-side paste service with no database, and no back-end code. The data is stored entirely in the shared link" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Icon from '@iconify/svelte'
|
||||||
|
import { fade } from 'svelte/transition'
|
||||||
|
|
||||||
|
export let isMarkdown: boolean
|
||||||
|
export let isPlainText: boolean
|
||||||
|
export let decompressed: string
|
||||||
|
export let htmlContent: string
|
||||||
|
let cssClass = ''
|
||||||
|
export { cssClass as class }
|
||||||
|
|
||||||
|
let copied = false
|
||||||
|
function copyCode() {
|
||||||
|
navigator.clipboard.writeText(decompressed)
|
||||||
|
copied = true
|
||||||
|
setTimeout(() => (copied = false), 500)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cssClass + ' relative'}>
|
||||||
|
{#if isMarkdown}
|
||||||
|
<div class="h-full p-2">
|
||||||
|
{@html htmlContent}
|
||||||
|
</div>
|
||||||
|
{:else if isPlainText}
|
||||||
|
<pre class="h-full">{decompressed}</pre>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
class="z-40 right-4 top-[5.25rem] absolute inline"
|
||||||
|
on:click={copyCode}
|
||||||
|
title="Copy code">
|
||||||
|
<Icon class="text-xl" icon="fluent:copy-24-regular" />
|
||||||
|
</button>
|
||||||
|
{#if copied}
|
||||||
|
<span class="absolute right-4 top-24" out:fade={{ duration: 500 }}>Copied!</span>
|
||||||
|
{/if}
|
||||||
|
<!-- code block -->
|
||||||
|
<pre class="h-full"><code>{@html htmlContent}</code></pre>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
@@ -62,12 +62,10 @@
|
|||||||
type="text"
|
type="text"
|
||||||
bind:value={filter}
|
bind:value={filter}
|
||||||
bind:this={inputFilter}
|
bind:this={inputFilter}
|
||||||
class={$$restProps.class ?? ''}
|
class={$$restProps.class ?? ''} />
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
class="absolute top-full left-0 w-full bg-gray-700 max-h-[90vh] overflow-y-auto"
|
class="absolute top-full left-0 w-full bg-gray-700 max-h-[90vh] overflow-y-auto"
|
||||||
bind:this={divContainer}
|
bind:this={divContainer}>
|
||||||
>
|
|
||||||
{#each filteredItems as item}
|
{#each filteredItems as item}
|
||||||
<div class="p-1 hover:bg-gray-600" on:click={() => selectItem(item)}>{item.text}</div>
|
<div class="p-1 hover:bg-gray-600" on:click={() => selectItem(item)}>{item.text}</div>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -78,8 +76,7 @@
|
|||||||
readonly
|
readonly
|
||||||
value={value?.text ?? ''}
|
value={value?.text ?? ''}
|
||||||
class={$$restProps.class ?? ''}
|
class={$$restProps.class ?? ''}
|
||||||
on:click={showOptions}
|
on:click={showOptions} />
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import ComboBox from './ComboBox.svelte'
|
import ComboBox from './ComboBox.svelte'
|
||||||
import Icon from '@iconify/svelte'
|
import Icon from '@iconify/svelte'
|
||||||
import { goto } from '$app/navigation'
|
import { goto } from '$app/navigation'
|
||||||
|
import hljs from 'highlight.js'
|
||||||
|
|
||||||
type Language = {
|
type Language = {
|
||||||
text: string
|
text: string
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
selectedLang.subscribe((lang) => {
|
selectedLang.subscribe((lang) => {
|
||||||
textWrap = lang === 'plt' || lang === 'md'
|
textWrap = lang === 'plaintext' || lang === 'markdown'
|
||||||
})
|
})
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
@@ -43,12 +44,16 @@
|
|||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (editor) {
|
if (editor) {
|
||||||
languages = CodeMirror.modeInfo
|
languages = (CodeMirror.modeInfo as unknown[])
|
||||||
.map((e: any) => ({
|
.map((e: any) => {
|
||||||
text: e.name,
|
let name = e.name
|
||||||
value: shorten(e.name),
|
if (name === 'Common Lisp') name = 'Lisp'
|
||||||
|
return {
|
||||||
|
text: name,
|
||||||
|
value: shorten(name),
|
||||||
data: { mime: e.mime, mode: e.mode },
|
data: { mime: e.mime, mode: e.mode },
|
||||||
}))
|
}
|
||||||
|
})
|
||||||
.filter((l: any) => l.value !== 'gflm') // Remove github flavored markdown, redundant with markdown
|
.filter((l: any) => l.value !== 'gflm') // Remove github flavored markdown, redundant with markdown
|
||||||
|
|
||||||
selectedLanguage =
|
selectedLanguage =
|
||||||
@@ -69,6 +74,25 @@
|
|||||||
await updateShareUrl()
|
await updateShareUrl()
|
||||||
isUrlInputVisible = true
|
isUrlInputVisible = true
|
||||||
await tick()
|
await tick()
|
||||||
|
urlInput.value = $shareUrl
|
||||||
|
urlInput.select()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showMarkdownLink() {
|
||||||
|
await updateShareUrl()
|
||||||
|
isUrlInputVisible = true
|
||||||
|
await tick()
|
||||||
|
urlInput.value = `[Paste snippet](${$shareUrl})`
|
||||||
|
urlInput.select()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showIframeCode() {
|
||||||
|
await updateShareUrl()
|
||||||
|
isUrlInputVisible = true
|
||||||
|
await tick()
|
||||||
|
// @ts-ignore
|
||||||
|
const height = Math.min(editor['doc'].height * 1.1 + 45, 500)
|
||||||
|
urlInput.value = `<iframe width="100%" height="${height}" frameborder="0" src="${$shareUrl}"></iframe>`
|
||||||
urlInput.select()
|
urlInput.select()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +108,7 @@
|
|||||||
|
|
||||||
async function goToPreview() {
|
async function goToPreview() {
|
||||||
await updateShareUrl()
|
await updateShareUrl()
|
||||||
goto($shareUrl)
|
await goto($shareUrl)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -94,11 +118,13 @@
|
|||||||
<input
|
<input
|
||||||
bind:this={urlInput}
|
bind:this={urlInput}
|
||||||
type="text"
|
type="text"
|
||||||
class="border border-gray-300 bg-transparent p-1 grow"
|
class="border border-gray-300 bg-transparent p-1 grow" />
|
||||||
value={$shareUrl}
|
<button class="button" on:click={copyUrl} title="Copy">
|
||||||
/>
|
<Icon class="text-xl" icon="fluent:copy-16-regular" />
|
||||||
<button class="button" on:click={copyUrl}>Copy</button>
|
</button>
|
||||||
<button class="button" on:click={closeUrlInput}>Close</button>
|
<button class="button" on:click={closeUrlInput} title="Close">
|
||||||
|
<Icon class="text-xl" icon="material-symbols:close" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-end gap-2">
|
||||||
@@ -106,11 +132,8 @@
|
|||||||
<ComboBox
|
<ComboBox
|
||||||
items={languages}
|
items={languages}
|
||||||
bind:value={selectedLanguage}
|
bind:value={selectedLanguage}
|
||||||
class="bg-gray-700 border border-gray-300 p-1"
|
class="bg-gray-700 border border-gray-300 p-1" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Show link input-->
|
|
||||||
<button class="button" on:click={showUrlInput}>Get Link</button>
|
|
||||||
|
|
||||||
<!-- Toggle text wrap -->
|
<!-- Toggle text wrap -->
|
||||||
<button class="button" title="Toggle text wrap" on:click={() => (textWrap = !textWrap)}>
|
<button class="button" title="Toggle text wrap" on:click={() => (textWrap = !textWrap)}>
|
||||||
@@ -121,8 +144,23 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<!-- Show link input -->
|
||||||
|
<button class="button" on:click={showUrlInput} title="Get sharing link">
|
||||||
|
<Icon class="text-xl" icon="ph:link-bold" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Show markdown link -->
|
||||||
|
<button class="button" on:click={showMarkdownLink} title="Get markdown link">
|
||||||
|
<Icon class="text-xl" icon="simple-icons:markdown" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Show HTML iframe -->
|
||||||
|
<button class="button" on:click={showIframeCode} title="Get iframe code">
|
||||||
|
<Icon class="text-xl" icon="fluent:code-24-regular" />
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- Switch to readonly view -->
|
<!-- Switch to readonly view -->
|
||||||
<button class="button" on:click={goToPreview}>
|
<button class="button" on:click={goToPreview} title="Switch to read-only view">
|
||||||
<Icon class="text-xl" icon="fluent:eye-12-regular" />
|
<Icon class="text-xl" icon="fluent:eye-12-regular" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-wrap justify-between items-center px-3 py-1 text-sm w-full z-10 shadow-md gap-2 font-mono bg-gray-700"
|
class="flex flex-wrap justify-between items-center px-3 py-1 text-sm w-full z-10 shadow-md gap-2 font-mono bg-gray-700">
|
||||||
>
|
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<h1 class="text-xl">Paste</h1>
|
<h1 class="text-xl">
|
||||||
|
<a href="/new" title="Create a new note">Paste</a>
|
||||||
|
</h1>
|
||||||
<span class="ml-8 text-xs">
|
<span class="ml-8 text-xs">
|
||||||
<a
|
<a href="/about" target="_blank">About</a>
|
||||||
href="/?l=md#G44EgKwOeI4yftHdG1GcYD0C2OgpAyPf6/ETlHECvFqN4PKltfWVNNq/dxrd9ewVthcNrRISpdBi1XTRGItQbvfun9ZUSnWU0lQGCcpkEJbBdA0TP9vjSaAFsO2p4LH9Adeibgx4TX7JBFZmhsOj7P6S8zax1FrwAo6ThHI9QsENX5XxzPeWwayeTAn2aH0JfzJBI06twNrEUJCGmyQ/QSTQ7RnOHAcRZsWhKwg8TWX1D6q/JyngVmhCYuxN6kMC+iX1DekfSKS0p+gLeM0nTMX64lnL3WfeDIHcoIFFW7lAc0Dc/G/MpzYT8CznnlTx5xQf/+t7UkXNNrbIqehgqM3BIp/9mypDdR2Zpwexg32yXST1ZrbTPIurASmrD1OYNrYwRJ8BV3e0WqOyQog/U258yp0VYvgQK3OWt4AAgFrTYw27HSWh9LkmD5lAtakv6/n0fj/z+tyTqEaU5PdchoyhVqKyqjEJ2/cXe+kD01BMTd/zor4kfwAE1jaN5RiLwagKFfibyqw1SXed+Ywf1urDGbT92ILFgpZ+4VpQg0yY1RwBbgVZwqmsUFbcfNLLJIticha+GM6vi5k9FHqXND1s57VyV1Q5IhAP4zRimzipR2rEkKpHjeRpAoJIa0qLaLkNfN8HgcCA0gec+bDZ206cfvqtdJqnkvNWhsTFGQw="
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
About
|
|
||||||
</a>
|
|
||||||
<a class="ml-4" href="https://git.scambier.xyz/scambier/paste" target="_blank">Source</a>
|
<a class="ml-4" href="https://git.scambier.xyz/scambier/paste" target="_blank">Source</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+17
-6
@@ -1,3 +1,5 @@
|
|||||||
|
import hljs from 'highlight.js'
|
||||||
|
|
||||||
export const slugify = (str: string) =>
|
export const slugify = (str: string) =>
|
||||||
str
|
str
|
||||||
.trim()
|
.trim()
|
||||||
@@ -10,22 +12,22 @@ export const slugify = (str: string) =>
|
|||||||
|
|
||||||
export const shorten = (name: string) => {
|
export const shorten = (name: string) => {
|
||||||
let n = slugify(name).replace('script', '-s').replace('python', 'py').replace('markdown', 'md')
|
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, '')
|
const nov = (s: string) => s[0] + s.substring(1).replace(/[aeiouy-]/g, '')
|
||||||
if (n.replace(/-/g, '').length <= 4) {
|
if (n.replace(/-/g, '').length <= 4) {
|
||||||
return n.replace(/-/g, '')
|
return n.replace(/-/g, '')
|
||||||
}
|
}
|
||||||
if (n.split('-').length >= 2) {
|
if (n.split('-').length >= 2) {
|
||||||
return n
|
return n
|
||||||
.split('-')
|
.split('-')
|
||||||
.map((x) => nov(x.substr(0, 2)))
|
.map((x) => nov(x.substring(0, 2)))
|
||||||
.join('')
|
.join('')
|
||||||
.substr(0, 4)
|
.substring(0, 4)
|
||||||
}
|
}
|
||||||
n = nov(n)
|
n = nov(n)
|
||||||
if (n.length <= 4) {
|
if (n.length <= 4) {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
return n.substr(0, 2) + n.substr(n.length - 2, 2)
|
return n.substring(0, 2) + n.substring(n.length - 2, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLangFromUrl() {
|
export function getLangFromUrl() {
|
||||||
@@ -34,5 +36,14 @@ export function getLangFromUrl() {
|
|||||||
|
|
||||||
export const byId = (id: string) => document.getElementById(id)
|
export const byId = (id: string) => document.getElementById(id)
|
||||||
|
|
||||||
export const staticMetaDescription =
|
export function isInIframe(): boolean {
|
||||||
'Paste is a client-side paste service with no database, and no back-end code. The data is stored entirely in the shared link'
|
return window !== window.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHljsLangFromShort(short: string): string {
|
||||||
|
const langs = hljs
|
||||||
|
.listLanguages()
|
||||||
|
.filter((l) => l !== 'taggerscript') // seriously? This overrides typescript.
|
||||||
|
.map((lang) => ({ lang, short: shorten(lang) }))
|
||||||
|
return langs.find((l) => l.short === short)?.lang ?? 'plaintext'
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export const prerender = true
|
export const prerender = true
|
||||||
export const ssr = false
|
export const ssr = false
|
||||||
|
export const trailingSlash = 'always'
|
||||||
+41
-37
@@ -8,38 +8,34 @@
|
|||||||
import rehypeStringify from 'rehype-stringify'
|
import rehypeStringify from 'rehype-stringify'
|
||||||
import hljs from 'highlight.js'
|
import hljs from 'highlight.js'
|
||||||
import 'highlight.js/styles/nord.min.css'
|
import 'highlight.js/styles/nord.min.css'
|
||||||
|
import { getHljsLangFromShort, getLangFromUrl, isInIframe } from '$lib/utils'
|
||||||
import TopBar from '../components/TopBar.svelte'
|
import TopBar from '../components/TopBar.svelte'
|
||||||
import Icon from '@iconify/svelte'
|
import Icon from '@iconify/svelte'
|
||||||
import { getLangFromUrl, staticMetaDescription } from '$lib/utils'
|
import CodeView from '../components/CodeView.svelte'
|
||||||
|
import { goto } from '$app/navigation'
|
||||||
|
|
||||||
let decompressed: string
|
let decompressed: string
|
||||||
let htmlContent: string
|
let htmlContent: string
|
||||||
let isMarkdown = false
|
let isMarkdown = false
|
||||||
let isPlainText = false
|
let isPlainText = false
|
||||||
|
|
||||||
// Meta description
|
|
||||||
$: metaDescription = (() => {
|
|
||||||
if (decompressed) {
|
|
||||||
const text = decompressed.slice(0, 100)
|
|
||||||
return text + (text.length === 100 ? '...' : '')
|
|
||||||
}
|
|
||||||
return staticMetaDescription
|
|
||||||
})()
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
// extract the part in the url after the hash
|
||||||
|
const hash = window.location.hash.slice(1)
|
||||||
|
if (hash) {
|
||||||
let lang = getLangFromUrl()
|
let lang = getLangFromUrl()
|
||||||
// Remove github flavored markdown, redundant with markdown
|
// Remove github flavored markdown, redundant with markdown
|
||||||
lang = lang === 'gflm' ? 'md' : lang
|
lang = lang === 'gflm' ? 'md' : lang
|
||||||
|
|
||||||
// extract the part in the url after the hash
|
// Get the correct language name for hljs
|
||||||
const hash = window.location.hash.slice(1)
|
lang = getHljsLangFromShort(lang)
|
||||||
if (hash) {
|
|
||||||
// decompress the data
|
// decompress the data
|
||||||
const { decompress } = await import('$lib/brotli')
|
const { decompress } = await import('$lib/brotli')
|
||||||
decompressed = await decompress(hash)
|
decompressed = await decompress(hash)
|
||||||
|
|
||||||
// Markdown
|
// Markdown
|
||||||
if (lang === 'md') {
|
if (lang === 'markdown') {
|
||||||
const html = await unified()
|
const html = await unified()
|
||||||
.use(remarkParse)
|
.use(remarkParse)
|
||||||
.use(remarkGfm)
|
.use(remarkGfm)
|
||||||
@@ -51,16 +47,16 @@
|
|||||||
htmlContent = html.toString()
|
htmlContent = html.toString()
|
||||||
}
|
}
|
||||||
// Plain text
|
// Plain text
|
||||||
else if (lang === 'plt' || !lang) {
|
else if (lang === 'plaintext' || !lang) {
|
||||||
isPlainText = true
|
isPlainText = true
|
||||||
}
|
}
|
||||||
// Code
|
// Code
|
||||||
else {
|
else {
|
||||||
htmlContent = hljs.highlight(lang, decompressed).value
|
htmlContent = hljs.highlight(decompressed, { language: lang }).value
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Redirect to editor page
|
// Redirect to editor page
|
||||||
window.location.href = '/editor'
|
await goto('/editor')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -70,35 +66,43 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="overflow-hidden h-screen flex flex-col">
|
<div class="overflow-hidden h-screen flex flex-col">
|
||||||
{#if htmlContent || decompressed}
|
{#if !isInIframe()}
|
||||||
<TopBar>
|
<TopBar>
|
||||||
<a
|
<a
|
||||||
href={'/editor' + getUrlDataPart()}
|
href={'/editor' + getUrlDataPart()}
|
||||||
class="p-1 hover:bg-gray-600/50"
|
class="p-1 hover:bg-gray-600/50"
|
||||||
title="Edit a copy of this note"
|
title="Edit a copy of this note">
|
||||||
>
|
|
||||||
<Icon class="text-xl" icon="fluent:document-edit-16-regular" />
|
<Icon class="text-xl" icon="fluent:document-edit-16-regular" />
|
||||||
</a>
|
</a>
|
||||||
</TopBar>
|
</TopBar>
|
||||||
<div class="overflow-y-auto grow">
|
|
||||||
<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 htmlContent}
|
|
||||||
{:else if isPlainText}
|
|
||||||
<div class="whitespace-pre-line">
|
|
||||||
{decompressed}
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<pre><code>{@html htmlContent}</code></pre>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<svelte:head>
|
{#if htmlContent || decompressed}
|
||||||
<meta name="description" property="og:description" content={metaDescription} />
|
<div class="overflow-y-auto grow pb-4">
|
||||||
</svelte:head>
|
{#if isInIframe()}
|
||||||
|
<CodeView
|
||||||
|
class="prose dark:prose-invert w-full max-w-full h-full"
|
||||||
|
{isMarkdown}
|
||||||
|
{isPlainText}
|
||||||
|
{decompressed}
|
||||||
|
{htmlContent} />
|
||||||
|
{:else}
|
||||||
|
<CodeView
|
||||||
|
class="prose dark:prose-invert lg:py-12 p-[0.5em] md:max-w-3xl md:mx-auto lg:max-w-4xl"
|
||||||
|
{isMarkdown}
|
||||||
|
{isPlainText}
|
||||||
|
{decompressed}
|
||||||
|
{htmlContent} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<footer
|
||||||
|
class="absolute bottom-0 left-0 w-full bg-gray-700 px-4 py-1 font-mono text-xs"
|
||||||
|
class:hidden={!isInIframe()}>
|
||||||
|
<a href={window.location.href} target="_blank">Powered by Paste</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
:global(pre code.hljs) {
|
:global(pre code.hljs) {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation'
|
||||||
|
|
||||||
|
goto(
|
||||||
|
'/?l=md#GysKICwPbMcLBD17GY3E2G8DSmoFwEZPDRIY+a4eP0EZJ8Cr1Qgu96X91MeoV4Jrh2wLgNl2heQ+bCycmlBrhRYqvgnDYuNne1vO0j0STTxGIh3Mm7dzpRVJqa7urrpgiUfYC+o8dGHVNxKJkFgsmxK110tv7u4wBMKS9h1GxeLkihii/V9yCyIib9H/WZByLMTCbTooCZTn58WCN8VUWVlKEiXp8UMkCs3aireAUbeJzahqKj8YsACPq+roiSpZbKr/1uuOZWV3SJ7ACiLF83VcZWYk5teMZLP3Eb+qIAHyJklcCeJLMN7dW1qhZ0xX0LTZQr7QBcRi7WgxlLRIicVpJJ5ls4Dee5JlIMgaNP4Z7X1VCpiwzVAUeQ4P5TnJvalhWe/GEcE5m5/W5SPQp8zhcU3DgKOvTD1RViuRxWakddH4v4R3AUdIi5Ov+vH1K2Lwvz4ECp5bCagC46ldwAI7T/vXdMUSBBJvq6K79b1OaYmIyzTrpdXPjZ3VTmVow4xrUj7W1V/nVvzybSGFP/PdoZ6HE5J3UprPpfEMKSNyZwmWz0RkRfE37lkPKrQfAdaARtsj8Oa+fohb+VSm+7oWQPHNSWp9w7OyewEqz0uIMYbMBLjmcxelEBzqVQ0RxZ4EN4IuPoCeXc2IUeROMJA2xSqlZVz4g+LRoXX99bwZfEm4+LQrIBO0OgFPpoFYN7YulcYvcT6HoWXfpW6B0n2OsC1c+hrubOXPuNlcmjUdlYeVx4xfrPxd6TTLAaUzXSPbbr3zoy0uDQkxVgJrUB8OZqWbebFZe1uie3up259eURT0mOyNWEzlvEIuhF9hoEVQByqiHCaG6TGESuutg+pRUEBbHss2OG/+VVUqk7OFc0H6E+tfCQrkF4/O4ZJ9QGmvh1+skE7Msns6TAmP+JmoBR52K4uAN+YkRtvC9xq9EoYUAiQ+n64Cs6fJ67UNAaU89DBZFxh2yEz9uye5uMAh26qErePAJNigv/9+f22Pe7DBbGvZRIFZV2i/Use/IBuRqkZs3CSw3FywVBvNUU5Sf/moD6N3/cmGuy/A/FonF4csOS5ctRQ70WerDvqKdq080+cCI4BpNvK4aX7f8mV7HI+afPdQIV+ifUwBLA=='
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { Editor } from 'codemirror'
|
import type { Editor } from 'codemirror'
|
||||||
import { debounce } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
import * as brotli from '$lib/brotli'
|
import * as brotli from '$lib/brotli'
|
||||||
import { byId, staticMetaDescription } from '$lib/utils'
|
import { byId } from '$lib/utils'
|
||||||
import TopBar from '../../components/TopBar.svelte'
|
import TopBar from '../../components/TopBar.svelte'
|
||||||
import { selectedLang, shareUrl } from '../../store'
|
import { selectedLang, shareUrl } from '../../store'
|
||||||
import EditForm from '../../components/EditForm.svelte'
|
import EditForm from '../../components/EditForm.svelte'
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
async function initCodeEditor() {
|
async function initCodeEditor() {
|
||||||
$selectedLang = new URLSearchParams(window.location.search).get('l') ?? ' plt'
|
$selectedLang = new URLSearchParams(window.location.search).get('l') ?? 'plt'
|
||||||
|
|
||||||
CodeMirror.modeURL = 'https://cdn.jsdelivr.net/npm/codemirror@5.65.16/mode/%N/%N.js'
|
CodeMirror.modeURL = 'https://cdn.jsdelivr.net/npm/codemirror@5.65.16/mode/%N/%N.js'
|
||||||
editor = new CodeMirror(byId('editor'), {
|
editor = new CodeMirror(byId('editor'), {
|
||||||
@@ -66,12 +66,10 @@
|
|||||||
editor.setValue(decompressed)
|
editor.setValue(decompressed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<meta name="description" property="og:description" content={staticMetaDescription} />
|
|
||||||
<script
|
<script
|
||||||
src="https://cdn.jsdelivr.net/combine/
|
src="https://cdn.jsdelivr.net/combine/
|
||||||
npm/codemirror@5.65.16,
|
npm/codemirror@5.65.16,
|
||||||
@@ -81,8 +79,13 @@ 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/mode/simple.min.js,
|
||||||
npm/codemirror@5.65.16/addon/scroll/simplescrollbars.js,
|
npm/codemirror@5.65.16/addon/scroll/simplescrollbars.js,
|
||||||
npm/codemirror@5.65.16/mode/meta.min.js"
|
npm/codemirror@5.65.16/mode/meta.min.js"
|
||||||
on:load={initCodeEditor}
|
on:load={initCodeEditor}></script>
|
||||||
></script>
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
href="https://cdn.jsdelivr.net/combine/
|
||||||
|
npm/codemirror@5.65.16/lib/codemirror.min.css,
|
||||||
|
npm/codemirror@5.65.16/theme/nord.min.css" />
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="flex flex-col font-mono h-screen bg-gray-700">
|
<div class="flex flex-col font-mono h-screen bg-gray-700">
|
||||||
@@ -106,7 +109,7 @@ npm/codemirror@5.65.16/mode/meta.min.js"
|
|||||||
<style>
|
<style>
|
||||||
:global(div.CodeMirror) {
|
:global(div.CodeMirror) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-size: 15px;
|
font-size: 14px;
|
||||||
font-family: 'Hack', monospace;
|
font-family: 'Hack', monospace;
|
||||||
}
|
}
|
||||||
#footer {
|
#footer {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation'
|
||||||
|
|
||||||
|
goto('/editor')
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user