This commit is contained in:
2023-10-29 17:25:22 +01:00
parent 906ead2d9a
commit 36bc353ff5
35 changed files with 1056 additions and 145 deletions
+3 -2
View File
@@ -1,9 +1,10 @@
{
"useTabs": true,
"useTabs": false,
"singleQuote": true,
"trailingComma": "none",
"trailingComma": "es5",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."],
"semi": false,
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}
+15 -1
View File
@@ -14,18 +14,32 @@
"devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.20.4",
"@types/codemirror": "^5.60.12",
"@types/lodash-es": "^4.17.10",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"autoprefixer": "^10.4.16",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.30.0",
"postcss": "^8.4.31",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1",
"svelte": "^4.0.5",
"svelte-check": "^3.4.3",
"tailwindcss": "^3.3.5",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.4.2"
},
"type": "module"
"type": "module",
"dependencies": {
"@sveltejs/adapter-static": "^2.0.3",
"brotli-compress": "^1.3.3",
"lodash-es": "^4.17.21",
"sass": "^1.69.5",
"slim-select": "^2.6.0",
"svelte-select": "^5.7.0",
"tw-elements": "^1.0.0"
}
}
+536 -114
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
+32 -9
View File
@@ -1,12 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<link rel="stylesheet" type="text/css" href="%sveltekit.assets%/fonts.css" />
<link
rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/combine/
npm/bootstrap@4.6.1/dist/css/bootstrap-grid.min.css,
npm/codemirror@5.65.5/lib/codemirror.min.css,
npm/codemirror@5.65.5/addon/scroll/simplescrollbars.css,
npm/codemirror@5.65.5/theme/dracula.min.css,
npm/microtip@0.2.2/microtip.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>
<script src="https://cdn.jsdelivr.net/combine/
npm/lzma@2.3.2/src/lzma.min.js,
npm/clipboard@2.0.11/dist/clipboard.min.js,
npm/micromodal@0.4.10/dist/micromodal.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>
</body>
</html>
+5
View File
@@ -0,0 +1,5 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind variants;
/* App layout */
+20
View File
@@ -0,0 +1,20 @@
<script lang="ts">
type Item = { text: string }
export let value: Item | null = null
export let items: Item[]
</script>
<select bind:value={value} class={ ($$restProps.class ?? '') }>
{#each items as item}
<option value={item}>
{item.text}
</option>
{/each}
</select>
<style lang="scss">
select {
appearance: none;
}
</style>
+121
View File
@@ -0,0 +1,121 @@
<script lang="ts">
import { onMount, tick } from 'svelte'
import { shorten } from '$lib/utils'
import Select from './Select.svelte'
import type { Editor } from 'codemirror'
type Language = {
text: string
value: string
data: { mime: string; mode: string }
}
export let editor: Editor
let languages: Language[] = []
let selectedLanguage: Language | null = null
let urlInput: HTMLInputElement
let isUrlInputVisible = false
$: setDefaultLanguage(languages)
$: {
const language = selectedLanguage?.data ?? { mime: null, mode: null }
// @ts-ignore
editor.setOption('mode', language.mime)
CodeMirror.autoLoadMode(editor, language.mode)
}
// const initLangSelector = () => {
// select = new SlimSelect({
// select: '#language',
// data: CodeMirror.modeInfo.map((e) => ({
// text: e.name,
// value: shorten(e.name),
// data: { mime: e.mime, mode: e.mode },
// })),
// settings: {
// openPosition: 'down',
// },
// events: {
// afterChange: (e) => {
// console.log(e)
// // const language = e.data || { mime: null, mode: null }
// // editor.setOption('mode', language.mime)
// // CodeMirror.autoLoadMode(editor, language.mode)
// // document.title =
// // e.text && e.text !== 'Plain Text' ? `NoPaste - ${e.text} code snippet` : 'NoPaste'
// },
// },
// })
// // Set lang selector
// const l = new URLSearchParams(window.location.search).get('l')
// }
onMount(() => {
languages = CodeMirror.modeInfo.map((e) => ({
text: e.name,
value: shorten(e.name),
data: { mime: e.mime, mode: e.mode },
}))
// initLangSelector()
})
function setDefaultLanguage(languages: Language[]) {
const lang = languages.find((e) => e.text.toLowerCase() === 'plain text')!
selectedLanguage = lang
}
async function showUrlInput() {
isUrlInputVisible = true
await tick()
urlInput.select()
}
async function copyUrl() {
urlInput.select()
await navigator.clipboard.writeText(urlInput.value)
closeUrlInput()
}
function closeUrlInput() {
isUrlInputVisible = false
}
</script>
<div class="flex justify-between p-2 text-sm relative">
<div>NoPaste</div>
<div class="flex justify-end gap-2">
<div>
<Select
items={languages}
bind:value={selectedLanguage}
class="bg-gray-700 border border-gray-300 p-1"
/>
</div>
<div>
<button on:click={showUrlInput}> Generate Link </button>
</div>
</div>
{#if isUrlInputVisible}
<div class="absolute w-full h-full bg-gray-700 top-0 left-0 p-2">
<div class="flex gap-2">
<input
bind:this={urlInput}
type="text"
class="border border-gray-300 bg-transparent p-1 grow"
value="url goes here"
/>
<button on:click={copyUrl}> Copy </button>
<button on:click={closeUrlInput}> Close </button>
</div>
</div>
{/if}
</div>
<style lang="scss">
button {
@apply text-sm border border-gray-300 p-1 hover:bg-gray-600/50;
}
</style>
+7
View File
@@ -0,0 +1,7 @@
declare class LZMA {
constructor(any): {}
compress: (value: string, level: number, callback: (result: number[]) => void) => void
decompress: (b: string, callback: (result: any) => void) => void
}
declare const CodeMirror: any
+20
View File
@@ -0,0 +1,20 @@
import { compress as brotliCompress, decompress as brotliDecompress } from 'brotli-compress'
const textEncoder = new TextEncoder()
const textDecoder = new TextDecoder()
async function compress(value: string): Promise<string> {
const data = textEncoder.encode(value)
const compressed = await brotliCompress(data)
return btoa(String.fromCharCode(...compressed))
}
async function decompress(b: string): Promise<string> {
const data = atob(b)
const bytes = Uint8Array.from(data, (c) => c.charCodeAt(0))
const decompressed = await brotliDecompress(bytes)
const decoded = textDecoder.decode(decompressed)
return decoded
}
export { compress, decompress }
+23
View File
@@ -0,0 +1,23 @@
// LZMA imported from <script> in index.html
const blob = new Blob(['importScripts("https://cdn.jsdelivr.net/npm/lzma@2.3.2/src/lzma_worker.min.js");']);
const lzma = new LZMA(window.URL.createObjectURL(blob));
async function compress(value: string): Promise<string> {
return new Promise((resolve, reject) => {
lzma.compress(value, 1, (numbers: number[]) => {
const bytes = new Uint8Array(numbers);
const b64 = btoa(String.fromCharCode(...bytes))
resolve(b64)
})
})
}
async function decompress(b: string): Promise<string> {
return new Promise((resolve, reject) => {
lzma.decompress(b, (result: any) => {
resolve(result)
})
})
}
export { compress, decompress }
+31
View File
@@ -0,0 +1,31 @@
export const slugify = (str: string) =>
str
.trim()
.toString()
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/\+/g, '-p')
.replace(/#/g, '-sharp')
.replace(/[^\w\-]+/g, '')
export const shorten = (name: string) => {
let n = slugify(name).replace('script', '-s').replace('python', 'py')
const nov = (s: string) => s[0] + s.substr(1).replace(/[aeiouy-]/g, '')
if (n.replace(/-/g, '').length <= 4) {
return n.replace(/-/g, '')
}
if (n.split('-').length >= 2) {
return n
.split('-')
.map((x) => nov(x.substr(0, 2)))
.join('')
.substr(0, 4)
}
n = nov(n)
if (n.length <= 4) {
return n
}
return n.substr(0, 2) + n.substr(n.length - 2, 2)
}
export const byId = (id: string) => document.getElementById(id)
+5
View File
@@ -0,0 +1,5 @@
<script lang="ts">
import '../app.postcss'
</script>
<slot />
+2
View File
@@ -0,0 +1,2 @@
export const prerender = true
export const ssr = false
+82 -2
View File
@@ -1,2 +1,82 @@
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
<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'
const algorithm: {
compress: (data: string) => Promise<string>
decompress: (data: string) => Promise<string>
} = brotli
const host = window.location.protocol + '//' + window.location.host + '/'
let editor: Editor | null = null
const readOnly = false
let charLen = 0
let compressed: string = ''
let waiting = false
$: shareUrl = host + compressed
const updateCompressed = debounce(async () => {
if (editor) {
compressed = await algorithm.compress(editor.getValue())
console.log(compressed)
}
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
console.log(editor)
if (readOnly) {
document.body.classList.add('readonly')
}
editor.on('change', async () => {
if (editor) {
waiting = true
charLen = editor.getValue().length
updateCompressed()
}
})
}
onMount(async () => {
initCodeEditor()
})
</script>
<div class="flex flex-col font-mono h-screen bg-gray-700">
{#if !!editor}
<TopBar {editor} />
{/if}
<div id="editor" class="grow overflow-hidden" />
{editor?.getValue()}
<div class="p-2 text-sm">
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%;
}
</style>
+116
View File
@@ -0,0 +1,116 @@
/* 0. extract fonts into `JetBrainsMono` folder */
/* 1. check fonts `urls` with your directory structure */
/* normal fonts */
@font-face {
font-family: JetBrainsMono;
font-style: normal;
font-weight: 100;
src: url("/fonts/JetBrainsMono-Thin.woff2") format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: normal;
font-weight: 200;
src: url("/fonts/JetBrainsMono-ExtraLight.woff2")
format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: normal;
font-weight: 300;
src: url("/fonts/JetBrainsMono-Light.woff2") format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: normal;
font-weight: 400;
src: url("/fonts/JetBrainsMono-Regular.woff2") format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: normal;
font-weight: 500;
src: url("/fonts/JetBrainsMono-Medium.woff2") format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: normal;
font-weight: 600;
src: url("/fonts/JetBrainsMono-SemiBold.woff2") format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: normal;
font-weight: 700;
src: url("/fonts/JetBrainsMono-Bold.woff2") format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: normal;
font-weight: 800;
src: url("/fonts/JetBrainsMono-ExtraBold.woff2")
format("woff2");
}
/* italic fonts */
@font-face {
font-family: JetBrainsMono;
font-style: italic;
font-weight: 100;
src: url("/fonts/JetBrainsMono-ThinItalic.woff2")
format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: italic;
font-weight: 200;
format("truetype");
src: url("/fonts/JetBrainsMono-ExtraLightItalic.woff2")
format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: italic;
font-weight: 300;
src: url("/fonts/JetBrainsMono-LightItalic.woff2")
format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: italic;
font-weight: 400;
src: url("/fonts/JetBrainsMono-Italic.woff2") format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: italic;
font-weight: 500;
src: url("/fonts/JetBrainsMono-MediumItalic.woff2")
format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: italic;
font-weight: 600;
format("truetype");
src: url("/fonts/JetBrainsMono-SemiBoldItalic.woff2")
format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: italic;
font-weight: 700;
src: url("/fonts/JetBrainsMono-BoldItalic.woff2")
format("woff2");
}
@font-face {
font-family: JetBrainsMono;
font-style: italic;
font-weight: 800;
format("truetype");
src: url("/fonts/JetBrainsMono-ExtraBoldItalic.woff2")
format("woff2");
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+14 -13
View File
@@ -1,18 +1,19 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';
// import adapter from '@sveltejs/adapter-auto'
import { vitePreprocess } from '@sveltejs/kit/vite'
import adapter from '@sveltejs/adapter-static'
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
};
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter(),
},
}
export default config;
export default config
+14
View File
@@ -0,0 +1,14 @@
/** @type {import('tailwindcss').Config} */
const defaultTheme = require('tailwindcss/defaultTheme')
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {
fontFamily: {
mono: ['"JetBrainsMono"', ...defaultTheme.fontFamily.mono],
},
},
},
plugins: [],
}
+4 -4
View File
@@ -1,6 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [sveltekit()]
});
plugins: [sveltekit()],
})