wip
parent
906ead2d9a
commit
36bc353ff5
@ -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" } }]
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
@ -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>
|
||||
|
@ -0,0 +1,5 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@tailwind variants;
|
||||
/* App layout */
|
@ -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>
|
@ -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>
|
@ -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
|
@ -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 }
|
@ -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 }
|
@ -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)
|
@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import '../app.postcss'
|
||||
</script>
|
||||
|
||||
<slot />
|
@ -0,0 +1,2 @@
|
||||
export const prerender = true
|
||||
export const ssr = false
|
@ -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>
|
||||
|
@ -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.
@ -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
|
||||
|
@ -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: [],
|
||||
}
|
@ -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()],
|
||||
})
|
||||
|
Loading…
Reference in New Issue