inital commit
This commit is contained in:
commit
de6021a735
|
@ -0,0 +1,13 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
|
@ -0,0 +1,30 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:svelte/recommended',
|
||||
'prettier'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
parser: 'svelte-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
|
@ -0,0 +1,13 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"pluginSearchDirs": ["."],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
# create-svelte
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
||||
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npm create svelte@latest
|
||||
|
||||
# create a new project in my-app
|
||||
npm create svelte@latest my-app
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To create a production version of your app:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "paste69-svelte",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||
"format": "prettier --plugin-search-dir . --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@skeletonlabs/skeleton": "^2.2.0",
|
||||
"@skeletonlabs/tw-plugin": "^0.2.1",
|
||||
"@sveltejs/adapter-auto": "^2.0.0",
|
||||
"@sveltejs/kit": "^1.20.4",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/node": "^20.8.2",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-svelte": "^2.30.0",
|
||||
"postcss": "^8.4.24",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier-plugin-svelte": "^2.10.1",
|
||||
"svelte": "^4.0.5",
|
||||
"svelte-check": "^3.4.3",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^4.4.2"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@sentry/node": "^7.73.0",
|
||||
"@ts-stack/markdown": "^1.5.0",
|
||||
"highlight.js": "^11.8.0",
|
||||
"mongodb": "^6.1.0",
|
||||
"random-words": "^2.0.0",
|
||||
"svelte-tabler": "^0.6.3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
const tailwindcss = require('tailwindcss');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
|
||||
const config = {
|
||||
plugins: [
|
||||
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
|
||||
tailwindcss(),
|
||||
//But others, like autoprefixer, need to run after,
|
||||
autoprefixer
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = config;
|
|
@ -0,0 +1,12 @@
|
|||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" class="w-full h-full py-[20px] px-[50px] dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body class="w-full h-full min-h-full" data-sveltekit-preload-data="hover" data-theme="skeleton">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
/* Write your global styles here, in PostCSS syntax */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
|
@ -0,0 +1,15 @@
|
|||
import { MongoClient } from 'mongodb';
|
||||
import { DB_URL } from '$env/static/private';
|
||||
import type PasteSchema from "./paste-schema";
|
||||
|
||||
const client = new MongoClient(DB_URL);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("paste69");
|
||||
|
||||
const pastes = db.collection<PasteSchema>("pastes");
|
||||
|
||||
export {
|
||||
db,
|
||||
pastes,
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export default interface PasteSchema {
|
||||
id: string;
|
||||
contents: string;
|
||||
highlight: string;
|
||||
encrypted: boolean;
|
||||
burnAfterReading: boolean;
|
||||
createdAt: Date;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { SENTRY_DSN } from '$env/static/private';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import type { HandleServerError } from '@sveltejs/kit';
|
||||
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN,
|
||||
});
|
||||
|
||||
interface ServerError extends Error {
|
||||
code?: string;
|
||||
}
|
||||
|
||||
export const handleError: HandleServerError = ({ error, event }) => {
|
||||
Sentry.captureException(error, { extra: { event } });
|
||||
|
||||
return {
|
||||
message: 'Uh oh! An unexpected error occurred.',
|
||||
code: (error as ServerError)?.code ?? '500',
|
||||
};
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns:serif="http://www.serif.com/"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1010.9 930.9"
|
||||
style="enable-background:new 0 0 1010.9 930.9;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#118979;}
|
||||
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#149C8B;}
|
||||
</style>
|
||||
<g transform="matrix(1,0,0,1,-1339,0)">
|
||||
<g transform="matrix(0.862194,0,0,1.01029,1279.19,36.8297)">
|
||||
<rect id="No-Bg" x="29.7" y="-109.9" serif:id="No Bg" class="st0" width="1251.7" height="1068.3">
|
||||
</rect>
|
||||
<g id="No-Bg1" serif:id="No Bg">
|
||||
<g transform="matrix(1.15983,0,0,0.989813,-103.977,-35.1696)">
|
||||
<g transform="matrix(1,0,0,1,-569.977,-312.934)">
|
||||
<path class="st1" d="M1730.3,724.8c0.8,148.9-23.4,272.5-72.7,370.6c-49.3,98.1-111.5,147.1-186.8,147.1
|
||||
c-84.1,0-152.9-44.4-206.6-133.3c-42.4-27.2-63.7-54.1-63.7-80.5c0-6.4,1.2-12.8,3.6-19.2c10.4-28,44-42,100.9-42
|
||||
c44,0,75.3,9.6,93.7,28.8c6.4,7.2,9.6,16.4,9.6,27.6c0,3.2-0.4,8.4-1.2,15.6c-0.8,7.2-1.2,12.4-1.2,15.6c0,11.2,4,20,12,26.4
|
||||
c5.6,22.4,18.4,33.2,38.4,32.4c17.6-0.8,36.8-27.2,57.6-79.3c20.8-52.1,32.8-104.9,36-158.6c-24.8,32.8-58.5,49.2-100.9,49.2
|
||||
c-66.5,0-121.3-26-164.6-78.1c-40-47.2-62.1-105.7-66.1-175.4c-3.2-56.1,14.8-122.9,54.1-200.6c44.8-90.5,96.5-135.7,155-135.7
|
||||
c92.1,0,165.4,34.6,219.8,103.9C1701.9,508.8,1729.5,603.9,1730.3,724.8z M1528.5,646.8c4-19.2,6-40.8,6-64.9
|
||||
c0-63.3-11.6-98.1-34.8-104.5c-21.6-5.6-43.6,8.8-66.1,43.2c-20,28.8-34,62.1-42,99.7c-5.6,24-8.4,48.4-8.4,73.3
|
||||
c0,64.9,17.2,100.9,51.7,108.1c17.6,4,36.4-12.4,56.5-49.2C1508.1,720.4,1520.5,685.2,1528.5,646.8z"/>
|
||||
</g>
|
||||
<g transform="matrix(7.4705,0,0,7.4705,-3607.69,-3511.85)">
|
||||
<path class="st2" d="M575.1,501.9c-1.4,3.8-6,5.8-13.8,5.8c-6,0-10.3-1.3-12.8-4c-0.9-1-1.3-2.2-1.3-3.6c0-0.5,0.1-1.3,0.2-2.2
|
||||
c0.1-0.9,0.2-1.7,0.2-2.2c0-1.4-0.5-2.6-1.6-3.5c-0.8-3.2-2.5-4.7-5.3-4.4c-2.4,0.1-5.1,3.7-7.9,10.8
|
||||
c-2.9,7.1-4.5,14.4-4.9,21.8c3.4-4.6,8-6.9,13.8-6.9c9.1,0,16.6,3.6,22.6,10.7c5.5,6.6,8.5,14.6,9.1,24
|
||||
c0.4,7.7-2,16.9-7.4,27.5c-6.1,12.4-13.2,18.6-21.2,18.6c-12.6,0-22.7-4.7-30.1-14.2c-7.5-9.5-11.3-22.5-11.4-39
|
||||
c-0.1-20.4,3.2-37.4,10-50.9c6.8-13.5,15.3-20.3,25.6-20.3c11.5,0,21,6.1,28.3,18.3c5.8,3.8,8.7,7.5,8.7,11
|
||||
C575.6,500.1,575.4,501,575.1,501.9z M549.4,555.2c0.8-3.2,1.2-6.5,1.2-9.9c0-9-2.4-14-7.1-15c-2.4-0.4-5,1.8-7.7,6.8
|
||||
c-2.3,4.4-4,9.3-5.1,14.7c-0.5,2.6-0.8,5.6-0.8,8.9c0,8.7,1.6,13.4,4.8,14.2c3,0.8,6-1.2,9.1-5.8
|
||||
C546.4,565,548.3,560.4,549.4,555.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1,13 @@
|
|||
<script lang="ts">
|
||||
import type { HTMLButtonAttributes } from "svelte/elements";
|
||||
|
||||
interface $$Props extends HTMLButtonAttributes {}
|
||||
</script>
|
||||
|
||||
<button
|
||||
{...$$restProps}
|
||||
on:click
|
||||
class="px-2 py-2 text-gray-100 border border-gray-300 bg-teal-500 hover:bg-teal-800 disabled:bg-gray-700 bg-opacity-30 transition-colors"
|
||||
>
|
||||
<slot />
|
||||
</button>
|
|
@ -0,0 +1,22 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import type { HTMLTextareaAttributes } from 'svelte/elements';
|
||||
|
||||
export let contents = '';
|
||||
let ref: HTMLTextAreaElement;
|
||||
|
||||
interface $$Props extends HTMLTextareaAttributes {
|
||||
contents: string;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
ref.focus();
|
||||
});
|
||||
</script>
|
||||
|
||||
<textarea
|
||||
class="w-full h-full bg-transparent border-none resize-none outline-none"
|
||||
bind:value={contents}
|
||||
bind:this={ref}
|
||||
{...$$restProps}
|
||||
/>
|
|
@ -0,0 +1,55 @@
|
|||
<script lang="ts">
|
||||
import { SlideToggle } from '@skeletonlabs/skeleton';
|
||||
|
||||
/** Exposes parent props to this component. */
|
||||
export let parent: any;
|
||||
|
||||
import { getModalStore } from '@skeletonlabs/skeleton';
|
||||
const modalStore = getModalStore();
|
||||
|
||||
// Form Data
|
||||
const formData = {
|
||||
encrypt: false,
|
||||
password: undefined,
|
||||
burnAfterReading: false,
|
||||
};
|
||||
|
||||
// Custom submit function to pass the response and close the modal.
|
||||
function onFormSubmit(): void {
|
||||
if ($modalStore[0].response) $modalStore[0].response(formData);
|
||||
modalStore.close();
|
||||
}
|
||||
|
||||
// Base Classes
|
||||
const cBase = 'card p-4 w-modal shadow-xl space-y-4';
|
||||
const cHeader = 'text-2xl font-bold';
|
||||
const cForm = 'border border-surface-500 p-4 space-y-4 rounded-container-token';
|
||||
</script>
|
||||
|
||||
{#if $modalStore[0]}
|
||||
<div class="modal-example-form {cBase}">
|
||||
<header class={cHeader}>Options for Saving</header>
|
||||
<article>Some extra options for saving. Encrypting your paste will make it require a password for anyone to open it. Burn after reading makes your paste viewable only once.</article>
|
||||
<form class="modal-form {cForm}">
|
||||
<div class="grid grid-cols-3">
|
||||
<div class="col-span-1">Encrypt</div>
|
||||
<SlideToggle name="slide" bind:checked={formData.encrypt} />
|
||||
</div>
|
||||
{#if formData.encrypt}
|
||||
<div class="grid grid-cols-3">
|
||||
<div class="col-span-1">Password</div>
|
||||
<input class="input col-span-2 px-2 py-1" type="password" bind:value={formData.password} placeholder="Encryption password..." />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="grid grid-cols-3">
|
||||
<div class="col-span-1">Burn After Reading</div>
|
||||
<SlideToggle name="slide" bind:checked={formData.burnAfterReading} />
|
||||
</div>
|
||||
</form>
|
||||
<!-- prettier-ignore -->
|
||||
<footer class="modal-footer {parent.regionFooter}">
|
||||
<button class="btn {parent.buttonNeutral}" on:click={parent.onClose}>{parent.buttonTextCancel}</button>
|
||||
<button class="btn {parent.buttonPositive}" on:click={onFormSubmit}>Save Paste</button>
|
||||
</footer>
|
||||
</div>
|
||||
{/if}
|
|
@ -0,0 +1,69 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { Copy, DeviceFloppy, TextPlus, CaretDown } from 'svelte-tabler';
|
||||
import { getModalStore, type ModalComponent, type ModalSettings } from '@skeletonlabs/skeleton';
|
||||
import logo from '$lib/assets/logo.svg';
|
||||
import Button from './Button.svelte';
|
||||
import MoreOptionsForm from './MoreOptionsForm.svelte';
|
||||
|
||||
const modalStore = getModalStore();
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let extraOptions = {
|
||||
encrypt: false,
|
||||
password: undefined as string | undefined,
|
||||
burnAfterReading: false,
|
||||
};
|
||||
|
||||
export let disableSave: boolean = false;
|
||||
export let disableCopy: boolean = false;
|
||||
export let disableMoreOptions: boolean = false;
|
||||
|
||||
const modalComponent: ModalComponent = {
|
||||
ref: MoreOptionsForm,
|
||||
}
|
||||
|
||||
const modalSettings: ModalSettings = {
|
||||
type: 'component',
|
||||
component: modalComponent,
|
||||
response: (data: typeof extraOptions) => {
|
||||
extraOptions = data;
|
||||
onSave();
|
||||
},
|
||||
}
|
||||
|
||||
const onSave = () => dispatch('save', extraOptions);
|
||||
const onNew = () => dispatch('new');
|
||||
const onCopy = () => dispatch('copy');
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row max-w-full md:min-w-[595px] bg-slate-800">
|
||||
<a
|
||||
href="/about"
|
||||
class="flex flex-col items-center justify-center py-2 px-8 bg-black bg-opacity-50"
|
||||
>
|
||||
<img class="w-8" src={logo} alt="Paste69 Logo" />
|
||||
</a>
|
||||
|
||||
<div class="flex flex-col items-center justify-center pt-4 pb-2 px-12 w-full">
|
||||
<div class="flex flex-row justify-between gap-2 w-full">
|
||||
<Button title="Save" disabled={disableSave} on:click={onSave}>
|
||||
<DeviceFloppy />
|
||||
</Button>
|
||||
<Button title="New" on:click={onNew}>
|
||||
<TextPlus />
|
||||
</Button>
|
||||
<Button title="Copy" disabled={disableCopy} on:click={onCopy}>
|
||||
<Copy />
|
||||
</Button>
|
||||
</div>
|
||||
<!-- More options button with horizontal line through the word -->
|
||||
<button
|
||||
disabled={disableMoreOptions}
|
||||
on:click={() => modalStore.trigger(modalSettings)}
|
||||
class="w-full text-center text-sm text-gray-300 disabled:text-gray-400 border-b border-gray-500 leading-[0.1em] mt-4 mb-2"
|
||||
>
|
||||
<span class="bg-slate-800 px-2">More Options</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
// place files you want to import through the `$lib` alias in this folder.
|
|
@ -0,0 +1,32 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import ToolBox from '$lib/components/ToolBox.svelte';
|
||||
import { ChevronRight } from 'svelte-tabler';
|
||||
|
||||
console.log($page);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Paste69 - {$page.status} error</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="absolute top-[23px] left-[5px]">
|
||||
<ChevronRight />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center justify-center h-screen">
|
||||
<div class="text-xl text-center">
|
||||
<h1 class="h1 mb-2 p-0">{$page.status}</h1>
|
||||
<p class="mb-2">{$page.error?.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fixed bottom-0 right-0 w-full md:w-auto">
|
||||
<ToolBox
|
||||
disableSave={true}
|
||||
disableCopy={true}
|
||||
disableMoreOptions={true}
|
||||
on:new={() => goto('/')}
|
||||
/>
|
||||
</div>
|
|
@ -0,0 +1,70 @@
|
|||
<script>
|
||||
import '../app.postcss';
|
||||
import { Modal, Toast, initializeStores } from '@skeletonlabs/skeleton';
|
||||
import {
|
||||
PUBLIC_GOOGLE_ANALYTICS_SITE_ID,
|
||||
PUBLIC_ACKEE_DOMAIN_ID,
|
||||
PUBLIC_ACKEE_URL,
|
||||
PUBLIC_MATOMO_SITE_ID,
|
||||
PUBLIC_MATOMO_URL,
|
||||
PUBLIC_PLAUSIBLE_DOMAIN,
|
||||
PUBLIC_PLAUSIBLE_URL
|
||||
} from '$env/static/public';
|
||||
|
||||
initializeStores();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
{#if PUBLIC_GOOGLE_ANALYTICS_SITE_ID}
|
||||
<script
|
||||
async
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${PUBLIC_GOOGLE_ANALYTICS_SITE_ID}`}
|
||||
></script>
|
||||
<script>
|
||||
{@html `window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', '${PUBLIC_GOOGLE_ANALYTICS_SITE_ID}');`}
|
||||
</script>
|
||||
{/if}
|
||||
|
||||
{#if PUBLIC_PLAUSIBLE_DOMAIN}
|
||||
<script
|
||||
defer
|
||||
data-domain={PUBLIC_PLAUSIBLE_DOMAIN}
|
||||
src={`${PUBLIC_PLAUSIBLE_URL}/js/script.js`}
|
||||
></script>
|
||||
{/if}
|
||||
|
||||
{#if PUBLIC_ACKEE_URL && PUBLIC_ACKEE_DOMAIN_ID}
|
||||
<script
|
||||
async
|
||||
src={`${PUBLIC_ACKEE_URL}/tracker.js`}
|
||||
data-ackee-server={PUBLIC_ACKEE_URL}
|
||||
data-ackee-domain-id={PUBLIC_ACKEE_DOMAIN_ID}
|
||||
></script>
|
||||
{/if}
|
||||
|
||||
{#if PUBLIC_MATOMO_URL && PUBLIC_MATOMO_SITE_ID}
|
||||
<script defer src={`${PUBLIC_MATOMO_URL}/matomo.js`}></script>
|
||||
<script>
|
||||
{@html `
|
||||
var _paq = window._paq = window._paq || [];
|
||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="${PUBLIC_MATOMO_URL}/";
|
||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||
_paq.push(['setSiteId', '${PUBLIC_MATOMO_SITE_ID}']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();`}
|
||||
</script>
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
<Modal />
|
||||
<Toast />
|
||||
<slot />
|
|
@ -0,0 +1,15 @@
|
|||
import { pastes } from "$db/index";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
// Check if the `copyFrom` query parameter is present
|
||||
const copyFrom = url.searchParams.get("copy-from");
|
||||
if (copyFrom) {
|
||||
// If it is, we need to fetch the paste from the database
|
||||
// and return it to the client.
|
||||
const paste = await pastes.findOne({ id: copyFrom });
|
||||
return {
|
||||
paste: structuredClone(paste),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import { ChevronRight } from 'svelte-tabler';
|
||||
import Editor from '$lib/components/Editor.svelte';
|
||||
import ToolBox from '$lib/components/ToolBox.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { getToastStore } from '@skeletonlabs/skeleton';
|
||||
import { paste } from '../stores/app';
|
||||
|
||||
const toastStore = getToastStore();
|
||||
|
||||
export let data: PageData;
|
||||
let contents = data.paste?.contents ?? '';
|
||||
|
||||
const newPaste = () => {
|
||||
contents = '';
|
||||
goto('/');
|
||||
};
|
||||
|
||||
const savePaste = async (event: any) => {
|
||||
const res = await fetch('/api/pastes', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
contents,
|
||||
...event.detail,
|
||||
})
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
const { id, highlight, burnAfterReading } = await res.json();
|
||||
if (burnAfterReading) {
|
||||
goto(`/${id}/created`);
|
||||
} else {
|
||||
goto(`/${id}.${highlight}`);
|
||||
}
|
||||
} else {
|
||||
toastStore.trigger({
|
||||
message: 'Failed to save paste.',
|
||||
background: 'variant-filled-error',
|
||||
autohide: false,
|
||||
})
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Paste69 - Paste, if you dare</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="absolute top-[23px] left-[5px]">
|
||||
<ChevronRight />
|
||||
</div>
|
||||
|
||||
<Editor
|
||||
placeholder="Paste something, type something, do something."
|
||||
bind:contents={contents}
|
||||
/>
|
||||
|
||||
<div class="fixed bottom-0 right-0 w-full md:w-auto">
|
||||
<ToolBox
|
||||
disableSave={!contents}
|
||||
disableCopy={true}
|
||||
on:save={savePaste}
|
||||
on:new={newPaste}
|
||||
/>
|
||||
</div>
|
|
@ -0,0 +1,29 @@
|
|||
import { error } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
import { pastes } from '$db/index';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
const [id, ext] = params.slug.split('.');
|
||||
|
||||
// Fetch the paste
|
||||
const paste = await pastes.findOne({ id });
|
||||
if (!paste) {
|
||||
throw error(404, 'Paste not found');
|
||||
}
|
||||
|
||||
// Build the response object
|
||||
const response = {
|
||||
id: paste.id,
|
||||
contents: paste.contents,
|
||||
encrypted: paste.encrypted,
|
||||
highlight: ext || paste.highlight,
|
||||
burnAfterReading: paste.burnAfterReading,
|
||||
}
|
||||
|
||||
// If the paste is set as burnAfterReading, delete it
|
||||
if (paste.burnAfterReading) {
|
||||
await pastes.deleteOne({ id });
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
|
@ -0,0 +1,108 @@
|
|||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import ToolBox from '$lib/components/ToolBox.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { highlight } from '$utils/hljs';
|
||||
import { markdown } from '$utils/markdown';
|
||||
import { getModalStore, getToastStore, type ModalSettings } from '@skeletonlabs/skeleton';
|
||||
import { sleep } from '$utils/index';
|
||||
|
||||
let codeRef: HTMLPreElement;
|
||||
|
||||
const modalStore = getModalStore();
|
||||
const toastStore = getToastStore();
|
||||
|
||||
export let data: PageData;
|
||||
let decryptedData: string | undefined = undefined;
|
||||
|
||||
// Select all the text in the code block when it is double clicked.
|
||||
const selectAll = () => {
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(codeRef);
|
||||
selection?.removeAllRanges();
|
||||
selection?.addRange(range);
|
||||
};
|
||||
|
||||
// If the highlight is set to 'md' or 'markdown', and the
|
||||
// query contains either 'render' or 'render=true'
|
||||
// then we will render the markdown.
|
||||
const renderMarkdown =
|
||||
(data.highlight === 'md' ||
|
||||
data.highlight === 'markdown') &&
|
||||
(($page.url.searchParams.has('render') && !$page.url.searchParams.get('render')) ||
|
||||
$page.url.searchParams.get('render') === 'true');
|
||||
|
||||
if (data.encrypted && !decryptedData) {
|
||||
let modalOptions: ModalSettings;
|
||||
|
||||
const onResponse = async (password?: string) => {
|
||||
if (!password || password.length === 0) {
|
||||
await sleep(500);
|
||||
return modalStore.trigger(modalOptions)
|
||||
};
|
||||
|
||||
// Use the /api/pastes/[id]/decrypt endpoint to decrypt the paste
|
||||
// and set the decryptedData variable to the result.
|
||||
const result = await fetch(`/api/pastes/${data.id}/decrypt`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ password }),
|
||||
});
|
||||
|
||||
if (result.ok) {
|
||||
const { contents: decrypted } = await result.json();
|
||||
decryptedData = decrypted;
|
||||
} else {
|
||||
modalStore.trigger(modalOptions);
|
||||
await sleep(500);
|
||||
const id = toastStore.trigger({
|
||||
message: 'Failed to decrypt paste.',
|
||||
background: 'variant-filled-error',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
modalOptions = {
|
||||
type: 'prompt',
|
||||
title: 'Encrypted Paste',
|
||||
body: 'Enter the password to decrypt the paste.',
|
||||
valueAttr: { type: 'password', required: 'true', placeholder: 'Password', class: 'modal-prompt-input input px-4 py-2' },
|
||||
response: onResponse,
|
||||
};
|
||||
|
||||
modalStore.trigger(modalOptions);
|
||||
}
|
||||
|
||||
$: contents = renderMarkdown ? markdown(decryptedData ?? data.contents) : highlight(decryptedData ?? data.contents, data.highlight);
|
||||
$: lineCount = contents.split('\n').length;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Paste69 - Paste {data.id}</title>
|
||||
</svelte:head>
|
||||
|
||||
{#if renderMarkdown}
|
||||
<!-- prettier-ignore -->
|
||||
<div class="markdown text-xl max-w-[90ch] pb-24">{@html contents}</div>
|
||||
{:else}
|
||||
<div class="absolute text-left text-gray-500 top-0 left-0 bottom-0 w-[45px] pt-[20px] pl-[5px]">
|
||||
{#each Array.from({ length: lineCount }) as _, i}
|
||||
<div>{i + 1}</div>
|
||||
{/each}
|
||||
</div>
|
||||
<pre class="pb-24" bind:this={codeRef} on:dblclick={() => selectAll()} ><code>{@html contents}</code></pre>
|
||||
{/if}
|
||||
|
||||
<div class="fixed bottom-0 right-0 w-full md:w-auto">
|
||||
<ToolBox
|
||||
disableSave={true}
|
||||
disableMoreOptions={true}
|
||||
disableCopy={data.encrypted}
|
||||
on:new={() => goto('/')}
|
||||
on:copy={() => goto(`/?copy-from=${data.id}`)}
|
||||
/>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
import { error } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
import { pastes } from '$db/index';
|
||||
import { SITE_URL } from '$env/static/private';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
const paste = await pastes.findOne({ id: params.slug });
|
||||
|
||||
if (!paste) {
|
||||
throw error(404, 'Paste not found');
|
||||
}
|
||||
|
||||
const pasteUrl = `${SITE_URL}/${paste.id}.${paste.highlight}`;
|
||||
|
||||
return {
|
||||
pasteUrl,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import { goto } from "$app/navigation";
|
||||
import ToolBox from "$lib/components/ToolBox.svelte";
|
||||
import { ChevronRight } from "svelte-tabler";
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Paste69 - Burnable paste created</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="absolute top-[18px] left-[5px]">
|
||||
<ChevronRight />
|
||||
</div>
|
||||
<div class="fixed bottom-0 right-0">
|
||||
<ToolBox
|
||||
disableSave={true}
|
||||
disableCopy={true}
|
||||
disableMoreOptions={true}
|
||||
on:new={() => goto('/')}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col w-full h-full justify-center items-center">
|
||||
<div class="text-center text-xl">
|
||||
<h1 class="h1 mb-6 text-center">Your burnable paste has been created.</h1>
|
||||
<p class="mb-2">Share the following link with someone, and once they view it, it will be deleted forever.</p>
|
||||
<a class="text-2xl underline" href={data.pasteUrl}>{data.pasteUrl}</a>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,118 @@
|
|||
<script lang="ts">
|
||||
import { Copy, DeviceFloppy, Moon, Sun, TextPlus } from 'svelte-tabler';
|
||||
import { storeHighlightJs } from '@skeletonlabs/skeleton';
|
||||
import { CodeBlock } from '@skeletonlabs/skeleton';
|
||||
import ToolBox from '$lib/components/ToolBox.svelte';
|
||||
import hljs from '$utils/hljs';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
storeHighlightJs.set(hljs);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Paste69 - About</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="fixed bottom-0 right-0 w-full md:w-auto">
|
||||
<ToolBox disableSave={true} disableCopy={true} on:new={() => goto('/')} />
|
||||
</div>
|
||||
|
||||
<div class="pl-8 max-w-[100ch] pb-24">
|
||||
<h1 class="h1 mb-2">Paste69</h1>
|
||||
|
||||
<p class="mb-4 mt-4">
|
||||
Paste69 is a pastebin service built with
|
||||
<a class="underline hover:text-gray-300" href="https://kit.svelte.dev">SvelteKit</a>. It's a simple, fast, and easy to use pastebin
|
||||
service based on HasteBin. Like HasteBin, it's also open source and can be found over on
|
||||
<a class="underline hover:text-gray-300" href="https://github.com/watzon/paste69">GitHub</a>.
|
||||
</p>
|
||||
|
||||
<p class="mb-4 mt-4">
|
||||
Code highlighting is handled with the help of <a class="underline hover:text-gray-300" href="https://highlightjs.org/">highlight.js</a>.
|
||||
So if you have any issues with language detection or missing languages, take it up with them.
|
||||
</p>
|
||||
|
||||
<h2 id="usage" class="h2 mt-4 mb-2"><a class="underline hover:text-gray-300" href="#usage">Usage</a></h2>
|
||||
|
||||
<p class="mb-4 mt-4">
|
||||
To create a paste, go <a class="underline hover:text-gray-300" href="/">home</a> or click the "New" button (<TextPlus
|
||||
class="inline-block w-4 h-4"
|
||||
/>) in the tool box in the bottom right corner of the page. Paste whatever text you want into
|
||||
the editor, and click the "Save" button (<DeviceFloppy class="inline-block w-4 h-4" />) to
|
||||
create the paste.
|
||||
</p>
|
||||
|
||||
<p class="mb-4 mt-4">
|
||||
To copy an existing paste, click the "Copy" button (<Copy class="inline-block w-4 h-4" />)
|
||||
in the tool box in the bottom right corner of the page. This will start a new paste with the
|
||||
contents of the existing paste.
|
||||
</p>
|
||||
|
||||
<h2 id="cli-script" class="h2 mt-4 mb-2"><a class="underline hover:text-gray-300" href="#cli-script">CLI Script</a></h2>
|
||||
|
||||
<p class="mb-4 mt-4">
|
||||
To make it easier to create pastes, a CLI script is available. The script can be found <a
|
||||
href="/paste69.sh">here</a
|
||||
>. To use the script:
|
||||
</p>
|
||||
|
||||
<CodeBlock language="bash" code={`$ curl -O https://0x45.st/paste69.sh && chmod +x paste69.sh<br />
|
||||
$ ./paste69.sh --help<br /><br />
|
||||
|
||||
Paste69 CLI script<br /><br />
|
||||
|
||||
Usage:<br />
|
||||
paste69 <file> [options]<br />
|
||||
cat <file> | paste69 [options]<br /><br />
|
||||
|
||||
Options:<br />
|
||||
-h, --help Show this help text<br />
|
||||
-r, --raw Return the raw JSON response<br />
|
||||
-c, --copy Copy the paste URL to the clipboard<br />`}></CodeBlock>
|
||||
|
||||
<p class="mb-4 mt-4">To create a paste with the script, simply pipe the contents of a file to the script:</p>
|
||||
|
||||
<CodeBlock language="bash" code={`$ cat file.md | ./paste69.sh<br />
|
||||
https://0x45.st/some-random-id.md`}></CodeBlock>
|
||||
|
||||
<h2 id="api" class="h2 mt-4 mb-2"><a class="underline hover:text-gray-300" href="#api">API</a></h2>
|
||||
|
||||
<p class="mb-4 mt-4">Paste69 has a simple API for creating and fetching pastes. The API is documented below.</p>
|
||||
|
||||
<h3 id="api-creating-a-paste" class="h3 mt-4 mb-2">
|
||||
<a class="underline hover:text-gray-300" href="#api-creating-a-paste">Creating a Paste</a>
|
||||
</h3>
|
||||
|
||||
<p class="mb-4 mt-4">
|
||||
To create a paste, send a POST request to <code>/api/pastes</code>
|
||||
{' '}
|
||||
with the following JSON body:
|
||||
</p>
|
||||
|
||||
<CodeBlock language="json" code={`{ "contents": "paste contents" }`}></CodeBlock>
|
||||
|
||||
<p class="mb-4 mt-4">If the paste was successfully created, the API will respond with the following JSON:</p>
|
||||
|
||||
<CodeBlock language="json" code={`{
|
||||
"id": "paste id",
|
||||
"contents": "paste contents",
|
||||
"highlight": "txt",
|
||||
"created_at": "2021-08-05T07:30:00.000Z",
|
||||
}`}></CodeBlock>
|
||||
|
||||
<h3 id="api-fetching-a-paste" class="h3 mt-4 mb-2">
|
||||
<a class="underline hover:text-gray-300" href="#api-fetching-a-paste">Fetching a Paste</a>
|
||||
</h3>
|
||||
|
||||
<p class="mb-4 mt-4">
|
||||
To fetch a paste, send a GET request to{' '}
|
||||
<code>/api/pastes/:id</code>. If the paste exists, the API will respond with the following JSON:
|
||||
</p>
|
||||
|
||||
<CodeBlock language="json" code={`{
|
||||
"id": "paste id",
|
||||
"contents": "paste contents",
|
||||
"highlight": "txt",
|
||||
"created_at": "2021-08-05T07:30:00.000Z",
|
||||
}`}></CodeBlock>
|
||||
</div>
|
|
@ -0,0 +1,44 @@
|
|||
import { detectLanguage } from "$utils/hljs";
|
||||
import { encrypt as doEncrypt } from "$utils/crypto";
|
||||
import { error, json, type RequestHandler } from "@sveltejs/kit";
|
||||
import { generate } from 'random-words';
|
||||
import { pastes } from "$db/index";
|
||||
import { SITE_URL } from "$env/static/private";
|
||||
|
||||
export const POST: RequestHandler = async ({ request }) => {
|
||||
const { contents, encrypt, password, burnAfterReading } = await request.json();
|
||||
const id = generate({ exactly: 3, join: '-' });
|
||||
|
||||
if (!contents || contents.length === 0) {
|
||||
throw error(400, 'No contents provided');
|
||||
}
|
||||
|
||||
let pasteContents: string = contents;
|
||||
const highlight = detectLanguage(contents) || 'txt';
|
||||
|
||||
if (encrypt && !password) {
|
||||
throw error(400, 'No password provided for encryption.');
|
||||
} else if (encrypt) {
|
||||
pasteContents = await doEncrypt(contents, password);
|
||||
}
|
||||
|
||||
const data = {
|
||||
id,
|
||||
url: `${SITE_URL}/${id}.${highlight}`,
|
||||
highlight,
|
||||
encrypted: !!encrypt,
|
||||
contents: pasteContents,
|
||||
burnAfterReading: !!burnAfterReading,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
const res = await pastes.insertOne(data);
|
||||
|
||||
if (!res.acknowledged) {
|
||||
throw error(500, 'Failed to create paste');
|
||||
}
|
||||
|
||||
return json(data, {
|
||||
status: 201,
|
||||
})
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
import { pastes } from "$db/index";
|
||||
import { error, json, type RequestHandler } from "@sveltejs/kit";
|
||||
|
||||
// Fetch the paste with the given ID, returning it as a JSON object.
|
||||
export const GET: RequestHandler = async ({ params }) => {
|
||||
const { id } = params;
|
||||
|
||||
const paste = await pastes.findOne({ id });
|
||||
|
||||
if (!paste) {
|
||||
throw error(404, 'Paste not found');
|
||||
}
|
||||
|
||||
if (paste.encrypted) {
|
||||
throw error(400, 'Paste is encrypted');
|
||||
}
|
||||
|
||||
if (paste.burnAfterReading) {
|
||||
await pastes.deleteOne({ id });
|
||||
}
|
||||
|
||||
const data = {
|
||||
id: paste.id,
|
||||
highlight: paste.highlight,
|
||||
contents: paste.contents,
|
||||
burnAfterReading: paste.burnAfterReading,
|
||||
createdAt: paste.createdAt,
|
||||
}
|
||||
|
||||
return json(data);
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
import { error, json, type RequestHandler } from "@sveltejs/kit";
|
||||
import { pastes } from "$db/index";
|
||||
import { decrypt } from "$utils/crypto";
|
||||
|
||||
// Decrypt the paste with the given ID using the provided password.
|
||||
export const POST: RequestHandler = async ({ params, request }) => {
|
||||
const { id } = params;
|
||||
const { password } = await request.json();
|
||||
|
||||
console.log(password);
|
||||
|
||||
const paste = await pastes.findOne({ id });
|
||||
|
||||
if (!paste) {
|
||||
throw error(404, 'Paste not found');
|
||||
}
|
||||
|
||||
if (!paste.encrypted) {
|
||||
throw error(400, 'Paste is not encrypted');
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
throw error(400, 'No password provided for decryption.');
|
||||
}
|
||||
|
||||
try {
|
||||
const contents = await decrypt(paste.contents, password);
|
||||
|
||||
return json({
|
||||
...paste,
|
||||
contents,
|
||||
});
|
||||
} catch {
|
||||
throw error(400, 'Invalid password');
|
||||
}
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import { writable } from 'svelte/store';
|
||||
import type PasteSchema from '$db/paste-schema';
|
||||
|
||||
export const paste = writable<PasteSchema>({
|
||||
id: '',
|
||||
contents: '',
|
||||
highlight: '',
|
||||
createdAt: new Date(),
|
||||
});
|
|
@ -0,0 +1,89 @@
|
|||
import { subtle } from 'crypto';
|
||||
|
||||
// for large strings, use this from https://stackoverflow.com/a/49124600
|
||||
const buff_to_base64 = (buff: Iterable<number>) =>
|
||||
btoa(new Uint8Array(buff).reduce((data, byte) => data + String.fromCharCode(byte), ''));
|
||||
|
||||
const base64_to_buf = (b64: string) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
||||
|
||||
const enc = new TextEncoder();
|
||||
const dec = new TextDecoder();
|
||||
|
||||
export async function encrypt(data: string, password: string) {
|
||||
const encryptedData = await encryptData(data, password);
|
||||
return encryptedData;
|
||||
}
|
||||
|
||||
export async function decrypt(data: string, password: string) {
|
||||
const decryptedData = await decryptData(data, password);
|
||||
if (!decryptedData) {
|
||||
throw new Error('Invalid password');
|
||||
}
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
const getPasswordKey = (password: string) =>
|
||||
subtle.importKey('raw', enc.encode(password), 'PBKDF2', false, ['deriveKey']);
|
||||
|
||||
const deriveKey = (passwordKey: CryptoKey, salt: Uint8Array, keyUsage: KeyUsage[]) =>
|
||||
subtle.deriveKey(
|
||||
{
|
||||
name: 'PBKDF2',
|
||||
salt: salt,
|
||||
iterations: 250000,
|
||||
hash: 'SHA-256'
|
||||
},
|
||||
passwordKey,
|
||||
{ name: 'AES-GCM', length: 256 },
|
||||
false,
|
||||
keyUsage
|
||||
);
|
||||
|
||||
async function encryptData(secretData: string, password: string) {
|
||||
try {
|
||||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const passwordKey = await getPasswordKey(password);
|
||||
const aesKey = await deriveKey(passwordKey, salt, ['encrypt']);
|
||||
const encryptedContent = await subtle.encrypt(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
iv: iv
|
||||
},
|
||||
aesKey,
|
||||
enc.encode(secretData)
|
||||
);
|
||||
|
||||
const encryptedContentArr = new Uint8Array(encryptedContent);
|
||||
const buff = new Uint8Array(salt.byteLength + iv.byteLength + encryptedContentArr.byteLength);
|
||||
buff.set(salt, 0);
|
||||
buff.set(iv, salt.byteLength);
|
||||
buff.set(encryptedContentArr, salt.byteLength + iv.byteLength);
|
||||
const base64Buff = buff_to_base64(buff);
|
||||
return base64Buff;
|
||||
} catch (e) {
|
||||
throw new Error('Invalid password');
|
||||
}
|
||||
}
|
||||
|
||||
async function decryptData(encryptedData: string, password: string) {
|
||||
try {
|
||||
const encryptedDataBuff = base64_to_buf(encryptedData);
|
||||
const salt = encryptedDataBuff.slice(0, 16);
|
||||
const iv = encryptedDataBuff.slice(16, 16 + 12);
|
||||
const data = encryptedDataBuff.slice(16 + 12);
|
||||
const passwordKey = await getPasswordKey(password);
|
||||
const aesKey = await deriveKey(passwordKey, salt, ['decrypt']);
|
||||
const decryptedContent = await subtle.decrypt(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
iv: iv
|
||||
},
|
||||
aesKey,
|
||||
data
|
||||
);
|
||||
return dec.decode(decryptedContent);
|
||||
} catch (e) {
|
||||
throw new Error('Invalid password');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import hljs from 'highlight.js';
|
||||
import { extensionMap, languages } from './languages';
|
||||
|
||||
// Theme
|
||||
import 'highlight.js/styles/github-dark.css';
|
||||
|
||||
const autoLanguages = ['js', 'ts', 'css', 'html', 'json', 'md', 'php', 'py', 'rb', 'rs', 'shell'];
|
||||
|
||||
for (const [lang, exts] of Object.entries(extensionMap)) {
|
||||
if (exts.length > 0) {
|
||||
// Remove leading underscore, replace other underscores with dashes
|
||||
const name = lang.replace(/^_/, '').replace(/_/g, '-');
|
||||
// @ts-expect-error: this is valid
|
||||
hljs.registerLanguage(name, languages[lang]!);
|
||||
hljs.registerAliases(exts, { languageName: lang });
|
||||
}
|
||||
}
|
||||
|
||||
export const detectLanguage = (code: string) => {
|
||||
return hljs.highlightAuto(code, autoLanguages).language;
|
||||
};
|
||||
|
||||
export const highlight = (code: string, lang: string | undefined = undefined) => {
|
||||
if (!lang) {
|
||||
return hljs.highlightAuto(code, autoLanguages).value;
|
||||
} else {
|
||||
return hljs.highlight(code, { language: lang }).value;
|
||||
}
|
||||
};
|
||||
|
||||
// Re-export the `hljs` object
|
||||
export default hljs;
|
|
@ -0,0 +1,2 @@
|
|||
// Async sleep
|
||||
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@ -0,0 +1,590 @@
|
|||
import _1c from 'highlight.js/lib/languages/1c';
|
||||
import abnf from 'highlight.js/lib/languages/abnf';
|
||||
import accesslog from 'highlight.js/lib/languages/accesslog';
|
||||
import actionscript from 'highlight.js/lib/languages/actionscript';
|
||||
import ada from 'highlight.js/lib/languages/ada';
|
||||
import angelscript from 'highlight.js/lib/languages/angelscript';
|
||||
import apache from 'highlight.js/lib/languages/apache';
|
||||
import applescript from 'highlight.js/lib/languages/applescript';
|
||||
import arcade from 'highlight.js/lib/languages/arcade';
|
||||
import arduino from 'highlight.js/lib/languages/arduino';
|
||||
import armasm from 'highlight.js/lib/languages/armasm';
|
||||
import asciidoc from 'highlight.js/lib/languages/asciidoc';
|
||||
import aspectj from 'highlight.js/lib/languages/aspectj';
|
||||
import autohotkey from 'highlight.js/lib/languages/autohotkey';
|
||||
import autoit from 'highlight.js/lib/languages/autoit';
|
||||
import avrasm from 'highlight.js/lib/languages/avrasm';
|
||||
import awk from 'highlight.js/lib/languages/awk';
|
||||
import axapta from 'highlight.js/lib/languages/axapta';
|
||||
import bash from 'highlight.js/lib/languages/bash';
|
||||
import basic from 'highlight.js/lib/languages/basic';
|
||||
import bnf from 'highlight.js/lib/languages/bnf';
|
||||
import brainfuck from 'highlight.js/lib/languages/brainfuck';
|
||||
import _c from 'highlight.js/lib/languages/c';
|
||||
import cal from 'highlight.js/lib/languages/cal';
|
||||
import capnproto from 'highlight.js/lib/languages/capnproto';
|
||||
import ceylon from 'highlight.js/lib/languages/ceylon';
|
||||
import clean from 'highlight.js/lib/languages/clean';
|
||||
import clojure_repl from 'highlight.js/lib/languages/clojure-repl';
|
||||
import clojure from 'highlight.js/lib/languages/clojure';
|
||||
import cmake from 'highlight.js/lib/languages/cmake';
|
||||
import coffeescript from 'highlight.js/lib/languages/coffeescript';
|
||||
import coq from 'highlight.js/lib/languages/coq';
|
||||
import cos from 'highlight.js/lib/languages/cos';
|
||||
import cpp from 'highlight.js/lib/languages/cpp';
|
||||
import crmsh from 'highlight.js/lib/languages/crmsh';
|
||||
import crystal from 'highlight.js/lib/languages/crystal';
|
||||
import csharp from 'highlight.js/lib/languages/csharp';
|
||||
import csp from 'highlight.js/lib/languages/csp';
|
||||
import css from 'highlight.js/lib/languages/css';
|
||||
import d from 'highlight.js/lib/languages/d';
|
||||
import dart from 'highlight.js/lib/languages/dart';
|
||||
import delphi from 'highlight.js/lib/languages/delphi';
|
||||
import diff from 'highlight.js/lib/languages/diff';
|
||||
import django from 'highlight.js/lib/languages/django';
|
||||
import dns from 'highlight.js/lib/languages/dns';
|
||||
import dockerfile from 'highlight.js/lib/languages/dockerfile';
|
||||
import dos from 'highlight.js/lib/languages/dos';
|
||||
import dsconfig from 'highlight.js/lib/languages/dsconfig';
|
||||
import dts from 'highlight.js/lib/languages/dts';
|
||||
import dust from 'highlight.js/lib/languages/dust';
|
||||
import ebnf from 'highlight.js/lib/languages/ebnf';
|
||||
import elixir from 'highlight.js/lib/languages/elixir';
|
||||
import elm from 'highlight.js/lib/languages/elm';
|
||||
import erb from 'highlight.js/lib/languages/erb';
|
||||
import erlang_repl from 'highlight.js/lib/languages/erlang-repl';
|
||||
import erlang from 'highlight.js/lib/languages/erlang';
|
||||
import excel from 'highlight.js/lib/languages/excel';
|
||||
import fix from 'highlight.js/lib/languages/fix';
|
||||
import flix from 'highlight.js/lib/languages/flix';
|
||||
import fortran from 'highlight.js/lib/languages/fortran';
|
||||
import fsharp from 'highlight.js/lib/languages/fsharp';
|
||||
import gams from 'highlight.js/lib/languages/gams';
|
||||
import gauss from 'highlight.js/lib/languages/gauss';
|
||||
import gcode from 'highlight.js/lib/languages/gcode';
|
||||
import gherkin from 'highlight.js/lib/languages/gherkin';
|
||||
import glsl from 'highlight.js/lib/languages/glsl';
|
||||
import gml from 'highlight.js/lib/languages/gml';
|
||||
import go from 'highlight.js/lib/languages/go';
|
||||
import golo from 'highlight.js/lib/languages/golo';
|
||||
import gradle from 'highlight.js/lib/languages/gradle';
|
||||
import graphql from 'highlight.js/lib/languages/graphql';
|
||||
import groovy from 'highlight.js/lib/languages/groovy';
|
||||
import haml from 'highlight.js/lib/languages/haml';
|
||||
import handlebars from 'highlight.js/lib/languages/handlebars';
|
||||
import haskell from 'highlight.js/lib/languages/haskell';
|
||||
import haxe from 'highlight.js/lib/languages/haxe';
|
||||
import hsp from 'highlight.js/lib/languages/hsp';
|
||||
import http from 'highlight.js/lib/languages/http';
|
||||
import hy from 'highlight.js/lib/languages/hy';
|
||||
import inform7 from 'highlight.js/lib/languages/inform7';
|
||||
import ini from 'highlight.js/lib/languages/ini';
|
||||
import irpf90 from 'highlight.js/lib/languages/irpf90';
|
||||
import isbl from 'highlight.js/lib/languages/isbl';
|
||||
import java from 'highlight.js/lib/languages/java';
|
||||
import javascript from 'highlight.js/lib/languages/javascript';
|
||||
import json from 'highlight.js/lib/languages/json';
|
||||
import julia_repl from 'highlight.js/lib/languages/julia-repl';
|
||||
import julia from 'highlight.js/lib/languages/julia';
|
||||
import kotlin from 'highlight.js/lib/languages/kotlin';
|
||||
import lasso from 'highlight.js/lib/languages/lasso';
|
||||
import latex from 'highlight.js/lib/languages/latex';
|
||||
import ldif from 'highlight.js/lib/languages/ldif';
|
||||
import leaf from 'highlight.js/lib/languages/leaf';
|
||||
import less from 'highlight.js/lib/languages/less';
|
||||
import lisp from 'highlight.js/lib/languages/lisp';
|
||||
import livecodeserver from 'highlight.js/lib/languages/livecodeserver';
|
||||
import livescript from 'highlight.js/lib/languages/livescript';
|
||||
import llvm from 'highlight.js/lib/languages/llvm';
|
||||
import lsl from 'highlight.js/lib/languages/lsl';
|
||||
import lua from 'highlight.js/lib/languages/lua';
|
||||
import makefile from 'highlight.js/lib/languages/makefile';
|
||||
import markdown from 'highlight.js/lib/languages/markdown';
|
||||
import mathematica from 'highlight.js/lib/languages/mathematica';
|
||||
import matlab from 'highlight.js/lib/languages/matlab';
|
||||
import maxima from 'highlight.js/lib/languages/maxima';
|
||||
import mel from 'highlight.js/lib/languages/mel';
|
||||
import mercury from 'highlight.js/lib/languages/mercury';
|
||||
import mipsasm from 'highlight.js/lib/languages/mipsasm';
|
||||
import mizar from 'highlight.js/lib/languages/mizar';
|
||||
import mojolicious from 'highlight.js/lib/languages/mojolicious';
|
||||
import monkey from 'highlight.js/lib/languages/monkey';
|
||||
import moonscript from 'highlight.js/lib/languages/moonscript';
|
||||
import n1ql from 'highlight.js/lib/languages/n1ql';
|
||||
import nestedtext from 'highlight.js/lib/languages/nestedtext';
|
||||
import nginx from 'highlight.js/lib/languages/nginx';
|
||||
import nim from 'highlight.js/lib/languages/nim';
|
||||
import nix from 'highlight.js/lib/languages/nix';
|
||||
import node_repl from 'highlight.js/lib/languages/node-repl';
|
||||
import nsis from 'highlight.js/lib/languages/nsis';
|
||||
import objectivec from 'highlight.js/lib/languages/objectivec';
|
||||
import ocaml from 'highlight.js/lib/languages/ocaml';
|
||||
import openscad from 'highlight.js/lib/languages/openscad';
|
||||
import oxygene from 'highlight.js/lib/languages/oxygene';
|
||||
import parser3 from 'highlight.js/lib/languages/parser3';
|
||||
import perl from 'highlight.js/lib/languages/perl';
|
||||
import pf from 'highlight.js/lib/languages/pf';
|
||||
import pgsql from 'highlight.js/lib/languages/pgsql';
|
||||
import php from 'highlight.js/lib/languages/php';
|
||||
import plaintext from 'highlight.js/lib/languages/plaintext';
|
||||
import pony from 'highlight.js/lib/languages/pony';
|
||||
import powershell from 'highlight.js/lib/languages/powershell';
|
||||
import processing from 'highlight.js/lib/languages/processing';
|
||||
import profile from 'highlight.js/lib/languages/profile';
|
||||
import prolog from 'highlight.js/lib/languages/prolog';
|
||||
import properties from 'highlight.js/lib/languages/properties';
|
||||
import protobuf from 'highlight.js/lib/languages/protobuf';
|
||||
import puppet from 'highlight.js/lib/languages/puppet';
|
||||
import purebasic from 'highlight.js/lib/languages/purebasic';
|
||||
import python_repl from 'highlight.js/lib/languages/python-repl';
|
||||
import python from 'highlight.js/lib/languages/python';
|
||||
import q from 'highlight.js/lib/languages/q';
|
||||
import qml from 'highlight.js/lib/languages/qml';
|
||||
import r from 'highlight.js/lib/languages/r';
|
||||
import reasonml from 'highlight.js/lib/languages/reasonml';
|
||||
import rib from 'highlight.js/lib/languages/rib';
|
||||
import roboconf from 'highlight.js/lib/languages/roboconf';
|
||||
import routeros from 'highlight.js/lib/languages/routeros';
|
||||
import rsl from 'highlight.js/lib/languages/rsl';
|
||||
import ruby from 'highlight.js/lib/languages/ruby';
|
||||
import ruleslanguage from 'highlight.js/lib/languages/ruleslanguage';
|
||||
import rust from 'highlight.js/lib/languages/rust';
|
||||
import sas from 'highlight.js/lib/languages/sas';
|
||||
import scala from 'highlight.js/lib/languages/scala';
|
||||
import scheme from 'highlight.js/lib/languages/scheme';
|
||||
import scilab from 'highlight.js/lib/languages/scilab';
|
||||
import scss from 'highlight.js/lib/languages/scss';
|
||||
import shell from 'highlight.js/lib/languages/shell';
|
||||
import smali from 'highlight.js/lib/languages/smali';
|
||||
import smalltalk from 'highlight.js/lib/languages/smalltalk';
|
||||
import sml from 'highlight.js/lib/languages/sml';
|
||||
import sqf from 'highlight.js/lib/languages/sqf';
|
||||
import sql from 'highlight.js/lib/languages/sql';
|
||||
import stan from 'highlight.js/lib/languages/stan';
|
||||
import stata from 'highlight.js/lib/languages/stata';
|
||||
import step21 from 'highlight.js/lib/languages/step21';
|
||||
import stylus from 'highlight.js/lib/languages/stylus';
|
||||
import subunit from 'highlight.js/lib/languages/subunit';
|
||||
import swift from 'highlight.js/lib/languages/swift';
|
||||
import taggerscript from 'highlight.js/lib/languages/taggerscript';
|
||||
import tap from 'highlight.js/lib/languages/tap';
|
||||
import tcl from 'highlight.js/lib/languages/tcl';
|
||||
import thrift from 'highlight.js/lib/languages/thrift';
|
||||
import tp from 'highlight.js/lib/languages/tp';
|
||||
import twig from 'highlight.js/lib/languages/twig';
|
||||
import typescript from 'highlight.js/lib/languages/typescript';
|
||||
import vala from 'highlight.js/lib/languages/vala';
|
||||
import vbnet from 'highlight.js/lib/languages/vbnet';
|
||||
import vbscript from 'highlight.js/lib/languages/vbscript';
|
||||
import verilog from 'highlight.js/lib/languages/verilog';
|
||||
import vhdl from 'highlight.js/lib/languages/vhdl';
|
||||
import vim from 'highlight.js/lib/languages/vim';
|
||||
import wasm from 'highlight.js/lib/languages/wasm';
|
||||
import wren from 'highlight.js/lib/languages/wren';
|
||||
import x86asm from 'highlight.js/lib/languages/x86asm';
|
||||
import xl from 'highlight.js/lib/languages/xl';
|
||||
import xml from 'highlight.js/lib/languages/xml';
|
||||
import xquery from 'highlight.js/lib/languages/xquery';
|
||||
import yaml from 'highlight.js/lib/languages/yaml';
|
||||
import zephir from 'highlight.js/lib/languages/zephir';
|
||||
|
||||
export const languages = {
|
||||
'1c': _1c,
|
||||
abnf,
|
||||
accesslog,
|
||||
actionscript,
|
||||
ada,
|
||||
angelscript,
|
||||
apache,
|
||||
applescript,
|
||||
arcade,
|
||||
arduino,
|
||||
armasm,
|
||||
asciidoc,
|
||||
aspectj,
|
||||
autohotkey,
|
||||
autoit,
|
||||
avrasm,
|
||||
awk,
|
||||
axapta,
|
||||
bash,
|
||||
basic,
|
||||
bnf,
|
||||
brainfuck,
|
||||
c: _c,
|
||||
cal,
|
||||
capnproto,
|
||||
ceylon,
|
||||
clean,
|
||||
clojure_repl,
|
||||
clojure,
|
||||
cmake,
|
||||
coffeescript,
|
||||
coq,
|
||||
cos,
|
||||
cpp,
|
||||
crmsh,
|
||||
crystal,
|
||||
csharp,
|
||||
csp,
|
||||
css,
|
||||
d,
|
||||
dart,
|
||||
delphi,
|
||||
diff,
|
||||
django,
|
||||
dns,
|
||||
dockerfile,
|
||||
dos,
|
||||
dsconfig,
|
||||
dts,
|
||||
dust,
|
||||
ebnf,
|
||||
elixir,
|
||||
elm,
|
||||
erb,
|
||||
erlang_repl,
|
||||
erlang,
|
||||
excel,
|
||||
fix,
|
||||
flix,
|
||||
fortran,
|
||||
fsharp,
|
||||
gams,
|
||||
gauss,
|
||||
gcode,
|
||||
gherkin,
|
||||
glsl,
|
||||
gml,
|
||||
go,
|
||||
golo,
|
||||
gradle,
|
||||
graphql,
|
||||
groovy,
|
||||
haml,
|
||||
handlebars,
|
||||
haskell,
|
||||
haxe,
|
||||
hsp,
|
||||
http,
|
||||
hy,
|
||||
inform7,
|
||||
ini,
|
||||
irpf90,
|
||||
isbl,
|
||||
java,
|
||||
javascript,
|
||||
json,
|
||||
julia_repl,
|
||||
julia,
|
||||
kotlin,
|
||||
lasso,
|
||||
latex,
|
||||
ldif,
|
||||
leaf,
|
||||
less,
|
||||
lisp,
|
||||
livecodeserver,
|
||||
livescript,
|
||||
llvm,
|
||||
lsl,
|
||||
lua,
|
||||
makefile,
|
||||
markdown,
|
||||
mathematica,
|
||||
matlab,
|
||||
maxima,
|
||||
mel,
|
||||
mercury,
|
||||
mipsasm,
|
||||
mizar,
|
||||
mojolicious,
|
||||
monkey,
|
||||
moonscript,
|
||||
n1ql,
|
||||
nestedtext,
|
||||
nginx,
|
||||
nim,
|
||||
nix,
|
||||
node_repl,
|
||||
nsis,
|
||||
objectivec,
|
||||
ocaml,
|
||||
openscad,
|
||||
oxygene,
|
||||
parser3,
|
||||
perl,
|
||||
pf,
|
||||
pgsql,
|
||||
php,
|
||||
plaintext,
|
||||
pony,
|
||||
powershell,
|
||||
processing,
|
||||
profile,
|
||||
prolog,
|
||||
properties,
|
||||
protobuf,
|
||||
puppet,
|
||||
purebasic,
|
||||
python_repl,
|
||||
python,
|
||||
q,
|
||||
qml,
|
||||
r,
|
||||
reasonml,
|
||||
rib,
|
||||
roboconf,
|
||||
routeros,
|
||||
rsl,
|
||||
ruby,
|
||||
ruleslanguage,
|
||||
rust,
|
||||
sas,
|
||||
scala,
|
||||
scheme,
|
||||
scilab,
|
||||
scss,
|
||||
shell,
|
||||
smali,
|
||||
smalltalk,
|
||||
sml,
|
||||
sqf,
|
||||
sql,
|
||||
stan,
|
||||
stata,
|
||||
step21,
|
||||
stylus,
|
||||
subunit,
|
||||
swift,
|
||||
taggerscript,
|
||||
tap,
|
||||
tcl,
|
||||
thrift,
|
||||
tp,
|
||||
twig,
|
||||
typescript,
|
||||
vala,
|
||||
vbnet,
|
||||
vbscript,
|
||||
verilog,
|
||||
vhdl,
|
||||
vim,
|
||||
wasm,
|
||||
wren,
|
||||
x86asm,
|
||||
xl,
|
||||
xml,
|
||||
xquery,
|
||||
yaml,
|
||||
zephir
|
||||
};
|
||||
|
||||
export const extensionMap: Record<string, string[]> = {
|
||||
'1c': [],
|
||||
abnf: [],
|
||||
accesslog: [],
|
||||
actionscript: ['as'],
|
||||
ada: ['ada', 'adb', 'ads'],
|
||||
angelscript: ['angelscript'],
|
||||
apache: ['apache'],
|
||||
applescript: ['app'],
|
||||
arcade: ['arcade'],
|
||||
arduino: ['ino'],
|
||||
armasm: ['s'],
|
||||
asciidoc: ['adoc'],
|
||||
aspectj: ['aj'],
|
||||
autohotkey: ['ahk'],
|
||||
autoit: ['au3'],
|
||||
avrasm: ['s'],
|
||||
awk: ['awk'],
|
||||
axapta: ['ax'],
|
||||
bash: ['sh', 'bash'],
|
||||
basic: ['bas'],
|
||||
bnf: ['bnf'],
|
||||
brainfuck: ['b', 'bf'],
|
||||
c: ['c'],
|
||||
cal: ['cal'],
|
||||
capnproto: ['capnp'],
|
||||
ceylon: ['ceylon'],
|
||||
clean: ['icl', 'dcl'],
|
||||
'clojure-repl': [],
|
||||
clojure: ['clj', 'cljc', 'cljx', 'cljs'],
|
||||
cmake: ['CMakeLists.txt', 'cmake'],
|
||||
coffeescript: ['coffee'],
|
||||
coq: ['v'],
|
||||
cos: ['cls', 'int'],
|
||||
cpp: ['cpp', 'cc', 'h', 'C', 'H', 'hpp'],
|
||||
crmsh: ['crm'],
|
||||
crystal: ['cr'],
|
||||
csharp: ['cs'],
|
||||
csp: ['csp'],
|
||||
css: ['css'],
|
||||
d: ['d'],
|
||||
dart: ['dart'],
|
||||
delphi: ['dpr', 'dpk', 'pas'],
|
||||
diff: ['diff', 'patch'],
|
||||
django: ['djhtml'],
|
||||
dns: ['zone', 'bind'],
|
||||
dockerfile: ['Dockerfile'],
|
||||
dos: ['bat', 'cmd'],
|
||||
dsconfig: ['dsconfig'],
|
||||
dts: ['dts'],
|
||||
dust: ['dust'],
|
||||
ebnf: ['ebnf'],
|
||||
elixir: ['ex', 'exs'],
|
||||
elm: ['elm'],
|
||||
erb: ['erb'],
|
||||
'erlang-repl': [],
|
||||
erlang: ['erl', 'hrl'],
|
||||
excel: ['xls', 'xlsx'],
|
||||
fix: ['fix'],
|
||||
flix: ['flix'],
|
||||
fortran: ['f', 'for', 'f90', 'f95'],
|
||||
fsharp: ['fs', 'fsi'],
|
||||
gams: ['gms'],
|
||||
gauss: ['gss', 'gms'],
|
||||
gcode: ['gcode'],
|
||||
gherkin: ['feature'],
|
||||
glsl: ['vert', 'frag', 'geom'],
|
||||
gml: ['gml'],
|
||||
go: ['go'],
|
||||
golo: ['golo'],
|
||||
gradle: ['gradle'],
|
||||
graphql: ['graphql', 'gql'],
|
||||
groovy: ['groovy'],
|
||||
haml: ['haml'],
|
||||
handlebars: ['hbs'],
|
||||
haskell: ['hs'],
|
||||
haxe: ['hx'],
|
||||
hsp: ['hsp'],
|
||||
http: ['http'],
|
||||
hy: ['hy'],
|
||||
inform7: ['ni', 'n'],
|
||||
ini: ['ini'],
|
||||
irpf90: ['f', 'f90'],
|
||||
isbl: ['isbl'],
|
||||
java: ['java'],
|
||||
javascript: ['js'],
|
||||
json: ['json', 'json5'],
|
||||
'julia-repl': [],
|
||||
julia: ['jl'],
|
||||
kotlin: ['kt', 'kts', 'ktm'],
|
||||
lasso: ['lasso', 'lassoapp'],
|
||||
latex: ['tex'],
|
||||
ldif: ['ldif'],
|
||||
leaf: ['leaf'],
|
||||
less: ['less'],
|
||||
lisp: ['lisp'],
|
||||
livecodeserver: ['lc', 'irev'],
|
||||
livescript: ['ls'],
|
||||
llvm: ['ll'],
|
||||
lsl: ['lsl'],
|
||||
lua: ['lua'],
|
||||
makefile: ['Makefile'],
|
||||
markdown: ['md', 'markdown'],
|
||||
mathematica: ['nb'],
|
||||
matlab: ['m'],
|
||||
maxima: ['mac', 'mc'],
|
||||
mel: ['mel'],
|
||||
mercury: ['m', 'mh', 'pro'],
|
||||
mipsasm: ['s'],
|
||||
mizar: ['miz'],
|
||||
mojolicious: ['ep'],
|
||||
monkey: ['monkey'],
|
||||
moonscript: ['moon'],
|
||||
n1ql: ['n1ql'],
|
||||
nestedtext: ['nt'],
|
||||
nginx: ['nginx.conf'],
|
||||
nim: ['nim'],
|
||||
nix: ['nix'],
|
||||
'node-repl': [],
|
||||
nsis: ['nsi', 'nsh'],
|
||||
objectivec: ['m', 'mm'],
|
||||
ocaml: ['ml', 'mli'],
|
||||
openscad: ['scad'],
|
||||
oxygene: ['oxygene'],
|
||||
parser3: ['parser3'],
|
||||
perl: ['pl', 'pm'],
|
||||
pf: ['pf'],
|
||||
pgsql: ['pgsql'],
|
||||
php: ['php', 'php5', 'php7', 'php8'],
|
||||
plaintext: ['txt'],
|
||||
pony: ['pony'],
|
||||
powershell: ['ps1', 'psm1'],
|
||||
processing: ['pde'],
|
||||
profile: [],
|
||||
prolog: ['pl', 'pro', 'P'],
|
||||
properties: ['properties'],
|
||||
protobuf: ['proto'],
|
||||
puppet: ['pp'],
|
||||
purebasic: ['pb', 'pbi'],
|
||||
'python-repl': [],
|
||||
python: ['py'],
|
||||
q: ['q'],
|
||||
qml: ['qml'],
|
||||
r: ['R'],
|
||||
reasonml: ['re', 'rei'],
|
||||
rib: ['rib'],
|
||||
roboconf: ['rbcf'],
|
||||
routeros: ['rsc'],
|
||||
rsl: ['rsl'],
|
||||
ruby: ['rb'],
|
||||
ruleslanguage: ['ruleslanguage'],
|
||||
rust: ['rs'],
|
||||
sas: ['sas'],
|
||||
scala: ['scala'],
|
||||
scheme: ['scm'],
|
||||
scilab: ['sci'],
|
||||
scss: ['scss'],
|
||||
shell: ['sh', 'bash', 'dash', 'ksh', 'csh', 'tcsh', 'zsh', 'fish'],
|
||||
smali: ['smali'],
|
||||
smalltalk: ['st'],
|
||||
sml: ['sml', 'sig', 'fun'],
|
||||
sqf: ['sqf'],
|
||||
sql: ['sql'],
|
||||
stan: ['stan'],
|
||||
stata: ['do', 'ado'],
|
||||
step21: ['stp'],
|
||||
stylus: ['styl'],
|
||||
subunit: ['subunit'],
|
||||
swift: ['swift'],
|
||||
taggerscript: ['taggerscript'],
|
||||
tap: ['tap'],
|
||||
tcl: ['tcl'],
|
||||
thrift: ['thrift'],
|
||||
tp: ['tp'],
|
||||
twig: ['twig'],
|
||||
typescript: ['ts'],
|
||||
vala: ['vala'],
|
||||
vbnet: ['vb'],
|
||||
vbscript: ['vbs'],
|
||||
verilog: ['v'],
|
||||
vhdl: ['vhdl'],
|
||||
vim: ['vim'],
|
||||
wasm: [],
|
||||
wren: ['wren'],
|
||||
x86asm: ['s'],
|
||||
xl: ['xl', 'ilst'],
|
||||
xml: ['xml', 'svg', 'dtd'],
|
||||
xquery: ['xy', 'xquery'],
|
||||
yaml: ['yaml', 'yml'],
|
||||
zephir: ['zep']
|
||||
};
|
||||
|
||||
// Take a language (full name or extension) as input, and
|
||||
// return the name of the language as it should
|
||||
// be displayed to the user.
|
||||
export function getLanguageName(lang: string) {
|
||||
if (lang in languages) {
|
||||
return lang;
|
||||
}
|
||||
|
||||
for (const [name, exts] of Object.entries(extensionMap)) {
|
||||
if (exts.includes(lang)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import { CodeBlock } from '@skeletonlabs/skeleton';
|
||||
import { Marked, Renderer } from '@ts-stack/markdown';
|
||||
import { highlight } from './hljs';
|
||||
import { getLanguageName } from './languages';
|
||||
|
||||
class PasteRenderer extends Renderer {
|
||||
override heading(text: string, level: number) {
|
||||
const margin = level === 1 ? 8 : 2;
|
||||
return `<h${level} class="h${level} mb-${margin} font-bold">${text}</h${level}>`;
|
||||
}
|
||||
|
||||
override paragraph(text: string): string {
|
||||
return `<p class="mt-0 mb-5">${text}</p>`;
|
||||
}
|
||||
|
||||
override link(href: string, title: string, text: string): string {
|
||||
return `<a href="${href}" title="${title}" target="_blank" rel="noopener noreferrer" class="underline">${text}</a>`;
|
||||
}
|
||||
|
||||
override list(body: string, ordered?: boolean | undefined): string {
|
||||
const tag = ordered ? 'ol' : 'ul';
|
||||
return `<${tag} class="list-disc mt-5 mb-5 pl-7">${body}</${tag}>`;
|
||||
}
|
||||
|
||||
override code(code: string, lang?: string | undefined): string {
|
||||
const highlighted = highlight(code, lang);
|
||||
const languageName = lang ? getLanguageName(lang) : '';
|
||||
return `<div class="codeblock overflow-hidden shadow bg-neutral-900/90 text-sm text-white rounded-container-token" data-testid="codeblock">
|
||||
<header class="codeblock-header text-xs text-white/50 uppercase flex justify-between items-center p-2 pl-4">
|
||||
<span class="codeblock-language">${languageName}</span>
|
||||
</header>
|
||||
<pre class="codeblock-pre whitespace-pre-wrap break-all p-4 pt-1"><code class="codeblock-code language-bash lineNumbers">${highlighted}</code></pre>
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
|
||||
Marked.setOptions({
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: true,
|
||||
smartLists: true,
|
||||
smartypants: false,
|
||||
renderer: new PasteRenderer(),
|
||||
});
|
||||
|
||||
export function markdown(text: string) {
|
||||
return Marked.parse(text);
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,23 @@
|
|||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// 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(),
|
||||
|
||||
alias: {
|
||||
'$db/*': './src/db/*',
|
||||
'$utils/*': './src/utils/*',
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
import { join } from 'path';
|
||||
import type { Config } from 'tailwindcss';
|
||||
|
||||
import { skeleton } from '@skeletonlabs/tw-plugin';
|
||||
|
||||
const config = {
|
||||
darkMode: 'class',
|
||||
content: [
|
||||
'./src/**/*.{html,js,svelte,ts}',
|
||||
join(require.resolve(
|
||||
'@skeletonlabs/skeleton'),
|
||||
'../**/*.{html,js,svelte,ts}'
|
||||
)
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
skeleton({
|
||||
themes: {
|
||||
preset: ['skeleton'],
|
||||
},
|
||||
}),
|
||||
],
|
||||
safelist: [
|
||||
'mb-8',
|
||||
]
|
||||
} satisfies Config;
|
||||
|
||||
export default config;
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true
|
||||
}
|
||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()]
|
||||
});
|
Loading…
Reference in New Issue