Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@tailwindcss/vite": "^4.1.17",
"@types/node": "^25.0.3",
"shiki": "^3.21.0",
"svelte": "^5.43.8",
"svelte-check": "^4.3.4",
"tailwindcss": "^4.1.17",
Expand Down Expand Up @@ -50,6 +51,7 @@
"codemirror": "^6.0.2",
"date-fns": "^4.1.0",
"dotenv": "^17.2.3",
"sharp": "^0.34.5",
"thememirror": "^2.0.1"
}
}
604 changes: 604 additions & 0 deletions frontend/pnpm-lock.yaml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions frontend/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
onlyBuiltDependencies:
- esbuild
- sharp
32 changes: 8 additions & 24 deletions frontend/src/lib/components/og/paste-preview.svelte
Original file line number Diff line number Diff line change
@@ -1,31 +1,15 @@
<script lang="ts">
let data = $props();

let { title, content } = data;

const MAX_LENGTH = 300;
const truncatedContent =
content.length > MAX_LENGTH
? content.substring(0, MAX_LENGTH) + "..."
: content;
let { title = "Untitled.ts" } = $props();
</script>

<div
class="flex flex-col justify-center items-center w-full relative h-full bg-neutral-900 p-[50px] font-sans"
>
<div class="w-full flex justify-center mb-2">
<h1 class="text-white text-6xl">
{title}
</h1>
</div>

<div class="flex flex-col w-full h-full bg-[#121212] font-sans">
<div
class="flex flex-row justify-start items-center w-full overflow-hidden pl-5"
class="flex items-center w-full h-24 px-10 bg-[#252525] border-b border-[#333]"
>
<p
class="text-neutral-500 text-3xl text-left leading-tight overflow-hidden"
>
{truncatedContent}
</p>
<div class="w-4 h-4 rounded-full bg-[#ff5f56] mr-2"></div>
<div class="w-4 h-4 rounded-full bg-[#ffbd2e] mr-2"></div>
<div class="w-4 h-4 rounded-full bg-[#27c93f] mr-10"></div>

<span class="text-[#858585] text-2xl tracking-tight">{title}</span>
</div>
</div>
6 changes: 2 additions & 4 deletions frontend/src/routes/[id]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,8 @@
<svelte:head>
<title>DevBin | {data.title}</title>
<meta property="og:title" content={`DevBin | ${data.title}`} />
<meta
property="og:image"
content={page.url.origin + `/paste/${page.params.id}/preview.png`}
/>
<meta property="og:image" content="/paste/{page.params.id}/preview.png" />
<meta name="twitter:card" content="summary_large_image" />
</svelte:head>

<div class="flex-col h-full flex">
Expand Down
57 changes: 48 additions & 9 deletions frontend/src/routes/paste/[id]/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,19 @@ import { fail, redirect } from "@sveltejs/kit";
export async function load({ params, request, getClientAddress, cookies }) {
const client_ip = getUserIpAddress(request, getClientAddress);
const { id: paste_id } = params;
let tokens: { edit_token: string; delete_token: string } = {
edit_token: "",
delete_token: "",
};
const storedTokens = cookies.get(paste_id);

const tokens: { edit_token: string; delete_token: string } = JSON.parse(
cookies.get(paste_id) || "",
);
if (storedTokens) {
try {
tokens = JSON.parse(cookies.get(paste_id) || "");
} catch (error) {
console.log("Error parsing token cookie:", error);
}
}

try {
const response = await ApiService.getPasteByUuidPastesPasteIdGet({
Expand Down Expand Up @@ -61,9 +70,23 @@ export const actions = {
const data = await request.formData();
const paste_id = data.get("id")?.toString() || "";
const paste_content = data.get("content")?.toString() || "";
const tokens: { edit_token: string; delete_token: string } = JSON.parse(
cookies.get(paste_id) || "",
);
let tokens: { edit_token: string; delete_token: string } = {
edit_token: "",
delete_token: "",
};
const storedTokens = cookies.get(paste_id);

if (storedTokens) {
try {
tokens = JSON.parse(cookies.get(paste_id) || "");
} catch (error) {
console.log("Error parsing token cookie:", error);
}
} else {
return fail(400, {
error: "Missing edit token",
});
}

const response = await ApiService.editPastePastesPasteIdPut({
path: {
Expand All @@ -89,9 +112,25 @@ export const actions = {
delete: async ({ cookies, request }) => {
const data = await request.formData();
const paste_id = data.get("id")?.toString() || "";
const tokens: { edit_token: string; delete_token: string } = JSON.parse(
cookies.get(paste_id) || "",
);

let tokens: { edit_token: string; delete_token: string } = {
edit_token: "",
delete_token: "",
};
const storedTokens = cookies.get(paste_id);

if (storedTokens) {
try {
tokens = JSON.parse(cookies.get(paste_id) || "");
} catch (error) {
console.log("Error parsing token cookie:", error);
}
} else {
return fail(400, {
error: "Missing delete token",
});
}

const response = await ApiService.deletePastePastesPasteIdDelete({
path: {
paste_id,
Expand Down
98 changes: 50 additions & 48 deletions frontend/src/routes/paste/[id]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<meta property="og:title" content={`DevBin | ${data.title}`} />
<meta
property="og:image"
content={page.url.origin + `/paste/${page.params.id}/preview.png`}
content={page.url.origin + `/paste/${data.id}/preview.png`}
/>
</svelte:head>

Expand Down Expand Up @@ -139,54 +139,56 @@
<Copy />
</div>
</button>
<div class:hidden={!deletePrompt}>
<p>Are you sure you want to delete this paste?</p>
</div>
<div class="flex flex-row gap-2">
<!-- edit actions -->
<button
onclick={() => (isEditing = false)}
class="button-primary"
formaction="?/edit"
class:hidden={!isEditing}
>
Save
</button>
<button
type={"button"}
onclick={() => (isEditing = !isEditing)}
class={`${isEditing ? "button-ghost" : "button-primary"}`}
class:hidden={deletePrompt}
>
{#if isEditing}
Cancel
{:else}
<Edit />
{/if}
</button>
{#if data.edit_token && data.delete_token}
<div class:hidden={!deletePrompt}>
<p>Are you sure you want to delete this paste?</p>
</div>
<div class="flex flex-row gap-2">
<!-- edit actions -->
<button
onclick={() => (isEditing = false)}
class="button-primary"
formaction="?/edit"
class:hidden={!isEditing}
>
Save
</button>
<button
type={"button"}
onclick={() => (isEditing = !isEditing)}
class={`${isEditing ? "button-ghost" : "button-primary"}`}
class:hidden={deletePrompt}
>
{#if isEditing}
Cancel
{:else}
<Edit />
{/if}
</button>

<!-- delete actions -->
<button
onclick={() => (deletePrompt = true)}
class="button-danger"
formaction="?/delete"
class:hidden={!deletePrompt}
>
Confirm
</button>
<button
onclick={() => (deletePrompt = !deletePrompt)}
class={`${deletePrompt ? "button-outline" : "button-danger"}`}
type="button"
class:hidden={isEditing}
>
{#if deletePrompt}
Cancel
{:else}
<Delete />
{/if}
</button>
</div>
<!-- delete actions -->
<button
onclick={() => (deletePrompt = true)}
class="button-danger"
formaction="?/delete"
class:hidden={!deletePrompt}
>
Confirm
</button>
<button
onclick={() => (deletePrompt = !deletePrompt)}
class={`${deletePrompt ? "button-outline" : "button-danger"}`}
type="button"
class:hidden={isEditing}
>
{#if deletePrompt}
Cancel
{:else}
<Delete />
{/if}
</button>
</div>
{/if}
</div>
</div>
<div class="gap-2 text-end mb-2"></div>
Expand Down
Loading