markdown carta, layout

This commit is contained in:
Torsten Simon 2025-04-30 11:28:52 +02:00
parent d3fb4275ba
commit c51137b59e
6 changed files with 1414 additions and 25 deletions

1343
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,9 @@
"dependencies": { "dependencies": {
"@nostr-dev-kit/ndk": "^2.14.5", "@nostr-dev-kit/ndk": "^2.14.5",
"@nostr-dev-kit/ndk-svelte": "^2.4.10", "@nostr-dev-kit/ndk-svelte": "^2.4.10",
"carta-md": "^4.9.0",
"daisyui": "^5.0.28", "daisyui": "^5.0.28",
"dompurify": "^3.2.5",
"qrcode": "^1.5.4" "qrcode": "^1.5.4"
} }
} }

View file

@ -1,2 +1,10 @@
@import 'tailwindcss'; @import 'tailwindcss';
@plugin "daisyui"; @plugin "daisyui";
body, html, body > div {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}

View file

@ -6,7 +6,14 @@
import { ndk, ndkReady, user } from '$lib/stores'; import { ndk, ndkReady, user } from '$lib/stores';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { login } from '$lib'; import { login } from '$lib';
import { Carta, Markdown, MarkdownEditor } from 'carta-md';
import 'carta-md/default.css'; /* Default theme */
import DOMPurify from 'dompurify';
// Create a new instance of Carta (you might also want to add a sanitizer if you're processing user input)
let carta = new Carta({
sanitizer: DOMPurify.sanitize
});
let reactions = writable([]); let reactions = writable([]);
let reacted = writable(window.localStorage.getItem(event.id)); let reacted = writable(window.localStorage.getItem(event.id));
let reaction = writable({}) let reaction = writable({})
@ -47,7 +54,8 @@
</script> </script>
<div class="comment w-full border p-2"> <div class="comment w-full border p-2">
<p>{event.content}</p> <Markdown value={event.content} {carta} />
<div class="flex gap-2 reactions"> <div class="flex gap-2 reactions">
{#if $reacted} {#if $reacted}
<span>👍 {$reactions.length}</span> <span>👍 {$reactions.length}</span>

View file

@ -5,8 +5,15 @@
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import QRCode from 'qrcode'; import QRCode from 'qrcode';
import { login } from '$lib'; import { login } from '$lib';
import { Carta, MarkdownEditor } from 'carta-md';
import 'carta-md/default.css'; /* Default theme */
import DOMPurify from 'dompurify';
let question = ''; // Create a new instance of Carta (you might also want to add a sanitizer if you're processing user input)
let carta = new Carta({
sanitizer: DOMPurify.sanitize
});
let question = writable('');
let questionId = writable(''); ; let questionId = writable(''); ;
let questionShortId = 0; let questionShortId = 0;
let qrCodeUrl = writable(''); let qrCodeUrl = writable('');
@ -30,7 +37,7 @@
questionShortId = 10000000 + Math.floor(Math.random() * 90000000); questionShortId = 10000000 + Math.floor(Math.random() * 90000000);
const event = new NDKEvent($ndk, { const event = new NDKEvent($ndk, {
kind: 1342, kind: 1342,
content: question, content: $question,
tags: [['d', questionShortId + '']] tags: [['d', questionShortId + '']]
}); });
await event.publish(); await event.publish();
@ -52,7 +59,7 @@
}, 1000); }, 1000);
} }
$effect(() => { $effect(() => {
if ($ndkReady) { if ($ndkReady) {
if ($ndk.activeUser) { if ($ndk.activeUser) {
@ -89,22 +96,18 @@
{#key $questionId} {#key $questionId}
{#if $questionId === ''} {#if $questionId === ''}
<div> <div>
<textarea <MarkdownEditor bind:value={$question} {carta} />
class="mb-4 w-full rounded border p-2" <button class="btn btn-primary rounded mt-5" onclick={postQuestion}> Post Question </button>
placeholder="Type your question here..."
bind:value={question}
></textarea>
<button class="btn btn-primary rounded" onclick={postQuestion}> Post Question </button>
</div> </div>
{:else} {:else}
<div class="qr-share mt-4"> <div class="qr-share mt-4">
<h2 class="text-xl font-bold">Share Your Question</h2> <h2 class="text-xl font-bold">Share Your Question</h2>
<h3 class="text-center short-id">{questionShortId}</h3>
<img src={$qrCodeUrl} alt="QR Code" class="mt-2" /> <img src={$qrCodeUrl} alt="QR Code" class="mt-2" />
<p class="mb-5 text-center">Share this QR code or link:</p> <p class="mb-1 text-center">Share this QR code or link:</p>
<p class="text-center"> <p class="text-center mb-2 text-xl">
<a href={`/q/${$questionId}`}>{`/q/`}<span class="font-bold">{questionShortId}</span></a> <a href={`/q/${$questionId}`}>{`${window.location.origin}/q/`}<span class="font-bold">{questionShortId}</span></a>
</p> </p>
<h3 class="text-center text-xl">{questionShortId}</h3>
<div class="mt-4"> <div class="mt-4">
<label for="timer" class="mb-2 block">Set Timer (seconds):</label> <label for="timer" class="mb-2 block">Set Timer (seconds):</label>
<input type="number" id="timer" class="rounded border p-2" bind:value={timer} /> <input type="number" id="timer" class="rounded border p-2" bind:value={timer} />
@ -127,11 +130,24 @@
</div> </div>
<style> <style>
/* Optional styling */
:global(.carta-font-code) {
font-family: 'Monaco', monospace;
font-size: 1.1rem;
line-height: 1.1rem;
letter-spacing: normal;
}
:global(.carta-input) {
height: 150px !important;
}
input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-inner-spin-button,
input[type='number']::-webkit-outer-spin-button { input[type='number']::-webkit-outer-spin-button {
-webkit-appearance: none; -webkit-appearance: none;
margin: 0; margin: 0;
} }
.short-id {
font-size: 60px;
}
.qr-share img { .qr-share img {
width: 100%; width: 100%;
border: 3px solid #eee; border: 3px solid #eee;

View file

@ -2,24 +2,28 @@
/** @type {import('./$types').PageProps} */ /** @type {import('./$types').PageProps} */
let { data } = $props(); let { data } = $props();
import { onMount } from 'svelte';
import Comment from '$lib/components/Comment.svelte'; import Comment from '$lib/components/Comment.svelte';
import { ndk, connected, ndkReady, user } from '$lib/stores'; import { ndk, ndkReady } from '$lib/stores';
import { NDKEvent } from '@nostr-dev-kit/ndk'; import { NDKEvent } from '@nostr-dev-kit/ndk';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { login } from '$lib'; import { Carta, Markdown, MarkdownEditor } from 'carta-md';
import 'carta-md/default.css'; /* Default theme */
import DOMPurify from 'dompurify';
// Create a new instance of Carta (you might also want to add a sanitizer if you're processing user input)
let carta = new Carta({
sanitizer: DOMPurify.sanitize
});
function submitComment() { function submitComment() {
const commentEvent = new NDKEvent($ndk, { const commentEvent = new NDKEvent($ndk, {
kind: 2222, kind: 2222,
content: comment, content: $comment,
tags: [['E', data.id]] tags: [['E', data.id]]
}); });
commentEvent.publish(); commentEvent.publish();
} }
let comment = ''; let comment = writable('');
let comments = writable([]); let comments = writable([]);
$effect(() => { $effect(() => {
@ -39,14 +43,15 @@
{#await $ndk.fetchEvent(data.id) then question} {#await $ndk.fetchEvent(data.id) then question}
{#if question} {#if question}
<div class="question mb-4 w-full rounded border p-4 text-xl"> <div class="question mb-4 w-full rounded border p-4 text-xl">
<h2>Question:<br />{question.content}</h2> <h2>Question:<br />
<Markdown {carta} value={question.content} />
</h2>
</div> </div>
<div class="mb-2 flex w-full flex-col items-center justify-center gap-2"> <div class="mb-2 flex w-full flex-col items-center justify-center gap-2">
<h1 class="text-xl">Ideensammlung</h1> <h1 class="text-xl">Ideensammlung</h1>
<textarea class="w-full border p-2" bind:value={comment} placeholder="Mein Kommentar" <MarkdownEditor bind:value={$comment} {carta} />
></textarea> <button class="btn btn-primary mb-10" onclick={() => submitComment()}>Absenden</button>
<button class="btn btn-success" onclick={() => submitComment()}>Absenden</button>
</div> </div>
{:else} {:else}
<p>Loading...</p> <p>Loading...</p>
@ -64,10 +69,17 @@
</div> </div>
<style> <style>
:global(.carta-input) {
height: 100px !important;
}
:global(.carta-editor) {
width: 100% !important;
}
.main-layout { .main-layout {
padding: 30px;
margin: auto; margin: auto;
width: 100vw; width: 100vw;
max-width: 600px; max-width: 700px;
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;