Use prettier-plugin-svelte and prettier-plugin-tailwindcss

This commit is contained in:
dtonon 2026-05-27 15:30:11 +01:00
parent 2f6ea767d1
commit b3e6533748
23 changed files with 109 additions and 98 deletions

View file

@ -7,3 +7,4 @@ bun.lockb
# Miscellaneous # Miscellaneous
/static/ /static/
src/app.html

View file

@ -0,0 +1,9 @@
{
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"overrides": [
{
"files": "*.svelte",
"options": { "parser": "svelte" }
}
]
}

View file

@ -190,7 +190,7 @@
<aside <aside
bind:this={asideEl} bind:this={asideEl}
class="flex-1 flex-col bg-white px-4 pt-4 pb-20 md:absolute md:top-6 md:right-0 md:z-10 md:h-[calc(100%-1.5rem)] md:flex-none md:rounded-tl-xl md:px-6 md:py-6 md:transition-all md:duration-200 min-[1540px]:rounded-tr-xl class="flex-1 flex-col bg-white px-4 pt-4 pb-20 min-[1540px]:rounded-tr-xl md:absolute md:top-6 md:right-0 md:z-10 md:h-[calc(100%-1.5rem)] md:flex-none md:rounded-tl-xl md:px-6 md:py-6 md:transition-all md:duration-200
{mobileActive ? 'flex' : 'hidden'} md:flex {mobileActive ? 'flex' : 'hidden'} md:flex
{expanded ? 'md:w-150 md:shadow-2xl' : 'md:w-80 md:shadow-lg'}" {expanded ? 'md:w-150 md:shadow-2xl' : 'md:w-80 md:shadow-lg'}"
> >

View file

@ -70,7 +70,7 @@
disabled={deleteState.busy} disabled={deleteState.busy}
rows="2" rows="2"
placeholder="Recorded with the deletion" placeholder="Recorded with the deletion"
class="mb-4 w-full resize-none rounded border border-neutral-200 px-3 py-2 text-sm focus:ring-1 focus:ring-brand focus:outline-none disabled:opacity-50" class="focus:ring-brand mb-4 w-full resize-none rounded border border-neutral-200 px-3 py-2 text-sm focus:ring-1 focus:outline-none disabled:opacity-50"
></textarea> ></textarea>
<div class="flex justify-end gap-2"> <div class="flex justify-end gap-2">

View file

@ -97,11 +97,11 @@
<div class="mx-auto max-w-6xl"> <div class="mx-auto max-w-6xl">
<div class="flex flex-wrap items-center justify-between gap-2 py-2"> <div class="flex flex-wrap items-center justify-between gap-2 py-2">
<h1 class="text-[1.65rem] text-brand leading-7">{title}</h1> <h1 class="text-brand text-[1.65rem] leading-7">{title}</h1>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button <button
onclick={onNewTopic} onclick={onNewTopic}
class="rounded bg-brand px-4 py-1.5 md:text-sm font-medium text-white hover:bg-brand-hover md:px-6" class="bg-brand hover:bg-brand-hover rounded px-4 py-1.5 font-medium text-white md:px-6 md:text-sm"
> >
New discussion New discussion
</button> </button>

View file

@ -47,7 +47,7 @@
onclick={onClose} onclick={onClose}
aria-label="Close" aria-label="Close"
disabled={joinState.busy} disabled={joinState.busy}
class="absolute right-3 top-3 text-2xl leading-none text-neutral-400 hover:text-neutral-700 disabled:opacity-50" class="absolute top-3 right-3 text-2xl leading-none text-neutral-400 hover:text-neutral-700 disabled:opacity-50"
> >
× ×
</button> </button>
@ -87,7 +87,7 @@
autocapitalize="off" autocapitalize="off"
spellcheck="false" spellcheck="false"
onkeydown={(e) => e.key === "Enter" && onRetry()} onkeydown={(e) => e.key === "Enter" && onRetry()}
class="mb-3 w-full rounded border border-neutral-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-brand disabled:opacity-50" class="focus:ring-brand mb-3 w-full rounded border border-neutral-200 px-3 py-2 text-sm focus:ring-1 focus:outline-none disabled:opacity-50"
/> />
{/if} {/if}
@ -104,7 +104,7 @@
type="button" type="button"
onclick={onRetry} onclick={onRetry}
disabled={joinState.busy || (joinState.codeRequired && !code.trim())} disabled={joinState.busy || (joinState.codeRequired && !code.trim())}
class="rounded bg-brand px-3 py-2 text-sm font-medium text-white hover:bg-brand-hover disabled:cursor-not-allowed disabled:opacity-50" class="bg-brand hover:bg-brand-hover rounded px-3 py-2 text-sm font-medium text-white disabled:cursor-not-allowed disabled:opacity-50"
> >
{joinState.busy ? "Trying…" : "Retry"} {joinState.busy ? "Trying…" : "Retry"}
</button> </button>

View file

@ -28,7 +28,7 @@
</script> </script>
<aside aria-label="Latest discussions"> <aside aria-label="Latest discussions">
<h2 class="pb-2 text-[1.5rem] leading-7 text-brand">Latest discussions</h2> <h2 class="text-brand pb-2 text-[1.5rem] leading-7">Latest discussions</h2>
{#if overviewStore.recent.length === 0} {#if overviewStore.recent.length === 0}
<p class="text-sm text-neutral-400"> <p class="text-sm text-neutral-400">
{overviewStore.loading ? "Loading…" : "Nothing yet."} {overviewStore.loading ? "Loading…" : "Nothing yet."}
@ -40,7 +40,7 @@
<li> <li>
<a href="/thread/{t.id}" class="group block py-3"> <a href="/thread/{t.id}" class="group block py-3">
<p <p
class="line-clamp-2 text-lg text-neutral-700 leading-5 group-hover:text-brand" class="group-hover:text-brand line-clamp-2 text-lg leading-5 text-neutral-700"
> >
{t.title} {t.title}
</p> </p>

View file

@ -30,12 +30,12 @@
</script> </script>
<aside <aside
class="hidden max-w-52 min-w-48 shrink-0 flex-col justify-between pl-8 pr-1 pb-8 md:flex md:pt-6" class="hidden max-w-52 min-w-48 shrink-0 flex-col justify-between pr-1 pb-8 pl-8 md:flex md:pt-6"
> >
<div> <div>
<a <a
href="/" href="/"
class="mb-6 mt-3 flex flex-col min-w-0 hover:opacity-90" class="mt-3 mb-6 flex min-w-0 flex-col hover:opacity-90"
aria-label="{name} — home" aria-label="{name} — home"
> >
{#if groupStore.data?.picture} {#if groupStore.data?.picture}
@ -45,26 +45,26 @@
class="max-w-[90%] shrink-0 object-cover" class="max-w-[90%] shrink-0 object-cover"
/> />
{/if} {/if}
<span class="text-[1.3rem] font-medium text-neutral-900 mt-2 leading-6" <span class="mt-2 text-[1.3rem] leading-6 font-medium text-neutral-900"
>{name}</span >{name}</span
> >
</a> </a>
<a <a
href="/" href="/"
class="flex items-center gap-2 py-1 hover:bg-neutral-100 text-neutral-700" class="flex items-center gap-2 py-1 text-neutral-700 hover:bg-neutral-100"
> >
{mode === "simple" ? "Discussions" : "Home"} {mode === "simple" ? "Discussions" : "Home"}
</a> </a>
{#if mode === "simple"} {#if mode === "simple"}
<div class="flex mt-6"> <div class="mt-6 flex">
<p class="text-sm text-neutral-500">{groupStore.data?.about ?? ""}</p> <p class="text-sm text-neutral-500">{groupStore.data?.about ?? ""}</p>
</div> </div>
{:else} {:else}
<nav class="flex-auto mt-6" aria-label="Rooms"> <nav class="mt-6 flex-auto" aria-label="Rooms">
<p <p
class="pb-1 text-xs font-semibold uppercase tracking-wider text-neutral-400" class="pb-1 text-xs font-semibold tracking-wider text-neutral-400 uppercase"
> >
Rooms Rooms
</p> </p>
@ -84,7 +84,7 @@
{#if activeAbout} {#if activeAbout}
<div class="mt-6"> <div class="mt-6">
<p <p
class="pb-1 text-xs font-semibold uppercase tracking-wider text-neutral-400" class="pb-1 text-xs font-semibold tracking-wider text-neutral-400 uppercase"
> >
About About
</p> </p>
@ -93,9 +93,9 @@
{/if} {/if}
{/if} {/if}
<nav class="flex-auto mt-6" aria-label="Resources"> <nav class="mt-6 flex-auto" aria-label="Resources">
<p <p
class="pb-1 text-xs font-semibold uppercase tracking-wider text-neutral-400" class="pb-1 text-xs font-semibold tracking-wider text-neutral-400 uppercase"
> >
Resources Resources
</p> </p>
@ -122,7 +122,7 @@
<button <button
type="button" type="button"
onclick={resumeDraft} onclick={resumeDraft}
class="w-full rounded bg-brand px-3 py-1.5 text-sm font-medium text-white hover:bg-brand-hover" class="bg-brand hover:bg-brand-hover w-full rounded px-3 py-1.5 text-sm font-medium text-white"
> >
Resume draft Resume draft
</button> </button>
@ -154,7 +154,7 @@
{:else} {:else}
<button <button
onclick={openLogin} onclick={openLogin}
class="mt-3 w-full rounded bg-brand px-3 py-1.5 font-medium text-sm text-white hover:bg-brand-hover" class="bg-brand hover:bg-brand-hover mt-3 w-full rounded px-3 py-1.5 text-sm font-medium text-white"
> >
Login Login
</button> </button>

View file

@ -97,7 +97,7 @@
type="button" type="button"
onclick={onClose} onclick={onClose}
aria-label="Close" aria-label="Close"
class="absolute right-3 top-3 text-2xl leading-none text-neutral-400 hover:text-neutral-700" class="absolute top-3 right-3 text-2xl leading-none text-neutral-400 hover:text-neutral-700"
> >
× ×
</button> </button>
@ -119,7 +119,7 @@
<button <button
onclick={handleExtension} onclick={handleExtension}
disabled={busy || !hasExtension} disabled={busy || !hasExtension}
class="w-full rounded bg-brand px-3 py-2 text-sm font-medium text-white hover:bg-brand-hover disabled:cursor-not-allowed disabled:opacity-50" class="bg-brand hover:bg-brand-hover w-full rounded px-3 py-2 text-sm font-medium text-white disabled:cursor-not-allowed disabled:opacity-50"
> >
{busy ? "Connecting…" : "Log in with extension"} {busy ? "Connecting…" : "Log in with extension"}
</button> </button>
@ -131,7 +131,7 @@
<button <button
type="button" type="button"
onclick={showNsecView} onclick={showNsecView}
class="mt-4 block w-full text-center text-sm text-brand hover:underline" class="text-brand mt-4 block w-full text-center text-sm hover:underline"
> >
Or log in using your nsec Or log in using your nsec
</button> </button>
@ -149,12 +149,12 @@
autocorrect="off" autocorrect="off"
spellcheck="false" spellcheck="false"
onkeydown={(e) => e.key === "Enter" && handleNsec()} onkeydown={(e) => e.key === "Enter" && handleNsec()}
class="w-full rounded border border-neutral-200 px-3 py-2 font-mono text-sm focus:outline-none focus:ring-1 focus:ring-brand disabled:opacity-50" class="focus:ring-brand w-full rounded border border-neutral-200 px-3 py-2 font-mono text-sm focus:ring-1 focus:outline-none disabled:opacity-50"
/> />
<button <button
onclick={handleNsec} onclick={handleNsec}
disabled={busy || !nsec.trim()} disabled={busy || !nsec.trim()}
class="mt-3 w-full rounded bg-brand px-3 py-2 text-sm font-medium text-white hover:bg-brand-hover disabled:cursor-not-allowed disabled:opacity-50" class="bg-brand hover:bg-brand-hover mt-3 w-full rounded px-3 py-2 text-sm font-medium text-white disabled:cursor-not-allowed disabled:opacity-50"
> >
{busy ? "Logging in…" : "Log in"} {busy ? "Logging in…" : "Log in"}
</button> </button>
@ -164,7 +164,7 @@
<button <button
type="button" type="button"
onclick={showExtensionView} onclick={showExtensionView}
class="mt-4 block w-full text-center text-sm text-brand hover:underline" class="text-brand mt-4 block w-full text-center text-sm hover:underline"
> >
Log in with extension Log in with extension
</button> </button>

View file

@ -119,14 +119,14 @@
<a <a
href="/" href="/"
onclick={onClose} onclick={onClose}
class="py-2 text-xl text-neutral-700 hover:text-brand" class="hover:text-brand py-2 text-xl text-neutral-700"
>{mode === "simple" ? "Discussions" : "Home"}</a >{mode === "simple" ? "Discussions" : "Home"}</a
> >
{#if mode === "full"} {#if mode === "full"}
<nav class="mt-4" aria-label="Rooms"> <nav class="mt-4" aria-label="Rooms">
<p <p
class="pb-1 font-semibold uppercase tracking-wider text-neutral-400" class="pb-1 font-semibold tracking-wider text-neutral-400 uppercase"
> >
Rooms Rooms
</p> </p>
@ -138,7 +138,7 @@
href="/room/{room.id}" href="/room/{room.id}"
onclick={onClose} onclick={onClose}
class="block py-1.5 text-xl class="block py-1.5 text-xl
{activeRoom === room.id ? 'text-brand' : 'text-neutral-700 hover:text-brand'}" {activeRoom === room.id ? 'text-brand' : 'hover:text-brand text-neutral-700'}"
> >
{room.name} {room.name}
</a> </a>
@ -148,7 +148,7 @@
<nav class="mt-4" aria-label="Resources"> <nav class="mt-4" aria-label="Resources">
<p <p
class="pb-1 font-semibold uppercase tracking-wider text-neutral-400" class="pb-1 font-semibold tracking-wider text-neutral-400 uppercase"
> >
Resources Resources
</p> </p>
@ -157,7 +157,7 @@
href="/resource/{r.slug}" href="/resource/{r.slug}"
onclick={onClose} onclick={onClose}
aria-current={activeResource === r.slug ? "page" : undefined} aria-current={activeResource === r.slug ? "page" : undefined}
class="block py-1.5 text-xl hover:text-brand class="hover:text-brand block py-1.5 text-xl
{activeResource === r.slug ? 'text-brand' : 'text-neutral-700'}" {activeResource === r.slug ? 'text-brand' : 'text-neutral-700'}"
>{r.title}</a >{r.title}</a
> >
@ -166,9 +166,8 @@
href="/contacts" href="/contacts"
onclick={onClose} onclick={onClose}
aria-current={contactsActive ? "page" : undefined} aria-current={contactsActive ? "page" : undefined}
class="block py-1.5 text-lg hover:text-brand class="hover:text-brand block py-1.5 text-lg
{contactsActive ? 'text-brand' : 'text-neutral-700'}" {contactsActive ? 'text-brand' : 'text-neutral-700'}">Contacts</a
>Contacts</a
> >
</nav> </nav>
@ -177,7 +176,7 @@
<button <button
type="button" type="button"
onclick={onResume} onclick={onResume}
class="w-full rounded bg-brand px-3 py-2 font-medium text-white hover:bg-brand-hover" class="bg-brand hover:bg-brand-hover w-full rounded px-3 py-2 font-medium text-white"
> >
Resume draft Resume draft
</button> </button>
@ -214,7 +213,7 @@
<button <button
type="button" type="button"
onclick={onLogin} onclick={onLogin}
class="w-full rounded bg-brand px-3 py-2 text-lg font-medium text-white hover:bg-brand-hover" class="bg-brand hover:bg-brand-hover w-full rounded px-3 py-2 text-lg font-medium text-white"
> >
Login Login
</button> </button>

View file

@ -8,7 +8,8 @@
// PUBLIC_TITLE always wins when set. Without it, simple mode shows the room's // PUBLIC_TITLE always wins when set. Without it, simple mode shows the room's
// own name; full mode has no single room, so it falls back to GROUP_ID. // own name; full mode has no single room, so it falls back to GROUP_ID.
const name = $derived( const name = $derived(
TITLE || (MODE === "simple" ? (groupStore.data?.name ?? GROUP_ID) : GROUP_ID), TITLE ||
(MODE === "simple" ? (groupStore.data?.name ?? GROUP_ID) : GROUP_ID),
); );
</script> </script>
@ -34,7 +35,8 @@
{name[0].toUpperCase()} {name[0].toUpperCase()}
</div> </div>
{/if} {/if}
<span class="truncate text-2xl font-medium text-neutral-900 md:text-[1.7rem]" <span
class="truncate text-2xl font-medium text-neutral-900 md:text-[1.7rem]"
>{name}</span >{name}</span
> >
</a> </a>

View file

@ -24,7 +24,7 @@
const targetName = $derived( const targetName = $derived(
MODE === "full" MODE === "full"
? (groupsStore.list.find((g) => g.id === activeGroup.id)?.name ?? ? (groupsStore.list.find((g) => g.id === activeGroup.id)?.name ??
groupStore.data?.name) groupStore.data?.name)
: groupStore.data?.name, : groupStore.data?.name,
); );
@ -151,7 +151,7 @@
{#if targetName} {#if targetName}
<p class="text-sm text-neutral-700">{targetName}</p> <p class="text-sm text-neutral-700">{targetName}</p>
{/if} {/if}
<h2 id="newdisc-title" class="text-2xl text-brand">New discussion</h2> <h2 id="newdisc-title" class="text-brand text-2xl">New discussion</h2>
</div> </div>
<button <button
type="button" type="button"
@ -179,7 +179,7 @@
<div> <div>
<label <label
for="newdisc-title-input" for="newdisc-title-input"
class="block text-xs font-semibold uppercase tracking-wider text-neutral-400 mb-1" class="mb-1 block text-xs font-semibold tracking-wider text-neutral-400 uppercase"
> >
Title Title
</label> </label>
@ -190,7 +190,7 @@
bind:value={draftState.title} bind:value={draftState.title}
disabled={draftState.publishing} disabled={draftState.publishing}
maxlength="72" maxlength="72"
class="w-full rounded border border-neutral-200 px-3 py-2 focus:outline-none focus:ring-1 focus:ring-brand disabled:opacity-50" class="focus:ring-brand w-full rounded border border-neutral-200 px-3 py-2 focus:ring-1 focus:outline-none disabled:opacity-50"
/> />
</div> </div>
@ -199,16 +199,16 @@
<div class="relative"> <div class="relative">
<label <label
for="newdisc-labels-input" for="newdisc-labels-input"
class="block text-xs font-semibold uppercase tracking-wider text-neutral-400 mb-1" class="mb-1 block text-xs font-semibold tracking-wider text-neutral-400 uppercase"
> >
Labels Labels
</label> </label>
<div <div
class="flex flex-wrap gap-1.5 items-center rounded border border-neutral-200 px-3 py-1.5 min-h-[2.5rem] focus-within:ring-1 focus-within:ring-brand" class="focus-within:ring-brand flex min-h-[2.5rem] flex-wrap items-center gap-1.5 rounded border border-neutral-200 px-3 py-1.5 focus-within:ring-1"
> >
{#each draftState.labels as l} {#each draftState.labels as l}
<span <span
class="inline-flex items-center gap-1 rounded-lg bg-accent px-2 py-0.5 text-sm text-white" class="bg-accent inline-flex items-center gap-1 rounded-lg px-2 py-0.5 text-sm text-white"
> >
{l} {l}
<button <button
@ -219,7 +219,7 @@
> >
</span> </span>
{/each} {/each}
<div class="relative flex-1 min-w-[6rem]"> <div class="relative min-w-[6rem] flex-1">
<input <input
id="newdisc-labels-input" id="newdisc-labels-input"
type="text" type="text"
@ -234,7 +234,7 @@
placeholder={draftState.labels.length === 0 placeholder={draftState.labels.length === 0
? "Click to choose…" ? "Click to choose…"
: ""} : ""}
class="relative z-10 w-full border-0 bg-transparent p-0 outline-none focus:ring-0 text-sm py-0.5 disabled:opacity-50" class="relative z-10 w-full border-0 bg-transparent p-0 py-0.5 text-sm outline-none focus:ring-0 disabled:opacity-50"
/> />
{#if suggestion} {#if suggestion}
<span <span
@ -250,14 +250,14 @@
</div> </div>
{#if suggestOpen && filtered.length > 0} {#if suggestOpen && filtered.length > 0}
<div <div
class="absolute left-0 right-0 z-20 mt-1 rounded border border-neutral-200 bg-white shadow-lg p-3 flex flex-wrap gap-1.5" class="absolute right-0 left-0 z-20 mt-1 flex flex-wrap gap-1.5 rounded border border-neutral-200 bg-white p-3 shadow-lg"
> >
{#each filtered as l} {#each filtered as l}
<button <button
type="button" type="button"
onmousedown={(e) => e.preventDefault()} onmousedown={(e) => e.preventDefault()}
onclick={() => onSuggestionClick(l)} onclick={() => onSuggestionClick(l)}
class="rounded-lg bg-accent hover:bg-accent-hover px-2.5 py-0.5 text-sm text-white" class="bg-accent hover:bg-accent-hover rounded-lg px-2.5 py-0.5 text-sm text-white"
> >
{l} {l}
</button> </button>
@ -304,7 +304,7 @@
disabled={draftState.publishing || disabled={draftState.publishing ||
!draftState.title.trim() || !draftState.title.trim() ||
!draftState.content.trim()} !draftState.content.trim()}
class="rounded bg-brand px-5 py-1.5 font-medium text-white hover:bg-brand-hover disabled:cursor-not-allowed disabled:opacity-50" class="bg-brand hover:bg-brand-hover rounded px-5 py-1.5 font-medium text-white disabled:cursor-not-allowed disabled:opacity-50"
> >
{draftState.publishing ? "Publishing…" : "Publish discussion"} {draftState.publishing ? "Publishing…" : "Publish discussion"}
</button> </button>

View file

@ -593,7 +593,7 @@
href={inline.href} href={inline.href}
target={internal ? undefined : "_blank"} target={internal ? undefined : "_blank"}
rel={internal ? undefined : "noopener noreferrer"} rel={internal ? undefined : "noopener noreferrer"}
class="text-brand hover:underline break-all">{inline.label}</a class="text-brand break-all hover:underline">{inline.label}</a
> >
{:else if inline.type === "mention"} {:else if inline.type === "mention"}
{@const u = profiles[inline.pubkey] ?? resolvedUsers[inline.pubkey]} {@const u = profiles[inline.pubkey] ?? resolvedUsers[inline.pubkey]}
@ -607,10 +607,10 @@
{@const u = profiles[inline.pubkey] ?? resolvedUsers[inline.pubkey]} {@const u = profiles[inline.pubkey] ?? resolvedUsers[inline.pubkey]}
<a <a
href="#post-{inline.eventId}" href="#post-{inline.eventId}"
class="no-underline font-normal leading-4 bg-neutral-100 block -ml-3 pl-3 py-1 hover:bg-neutral-200" class="-ml-3 block bg-neutral-100 py-1 pl-3 leading-4 font-normal no-underline hover:bg-neutral-200"
>{u?.shortName ?? inline.pubkey.slice(0, 8)} said >{u?.shortName ?? inline.pubkey.slice(0, 8)} said
<svg <svg
class="inline w-3 mb-0.5" class="mb-0.5 inline w-3"
viewBox="0 0 800 800" viewBox="0 0 800 800"
version="1.1" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -628,7 +628,7 @@
href="https://njump.me/{inline.entity}" href="https://njump.me/{inline.entity}"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="text-brand hover:underline break-all">{inline.label}</a class="text-brand break-all hover:underline">{inline.label}</a
> >
{:else if inline.type === "strong"} {:else if inline.type === "strong"}
<strong>{@render renderInlines(inline.children)}</strong> <strong>{@render renderInlines(inline.children)}</strong>
@ -679,11 +679,11 @@
src={block.src} src={block.src}
alt={block.alt} alt={block.alt}
loading="lazy" loading="lazy"
class="block -mx-6 w-[calc(100%_+_3rem)] max-w-none max-h-[80vh] object-contain rounded-none md:mx-auto md:w-full md:max-w-full md:rounded" class="-mx-6 block max-h-[80vh] w-[calc(100%_+_3rem)] max-w-none rounded-none object-contain md:mx-auto md:w-full md:max-w-full md:rounded"
/> />
{:else if block.type === "blockquote"} {:else if block.type === "blockquote"}
<blockquote <blockquote
class="my-3 pb-1 mb-3 border-l-3 border-neutral-200 pl-3 text-neutral-500" class="my-3 mb-3 border-l-3 border-neutral-200 pb-1 pl-3 text-neutral-500"
> >
{@render renderBlocks(block.blocks)} {@render renderBlocks(block.blocks)}
</blockquote> </blockquote>
@ -713,7 +713,7 @@
{:else if block.type === "table"} {:else if block.type === "table"}
{@const headRows = block.rows.filter((r) => r.head)} {@const headRows = block.rows.filter((r) => r.head)}
{@const bodyRows = block.rows.filter((r) => !r.head)} {@const bodyRows = block.rows.filter((r) => !r.head)}
<div class="overflow-x-auto my-4"> <div class="my-4 overflow-x-auto">
<table> <table>
{#if headRows.length} {#if headRows.length}
<thead> <thead>
@ -752,7 +752,7 @@
{/snippet} {/snippet}
<div <div
class="prose leading-5 max-w-none text-neutral-700 [&_p]:my-3 [&_img]:my-5 [&_blockquote_img]:mx-0 [&_blockquote_img]:w-full [&_blockquote_img]:max-w-full [&_blockquote_img]:rounded [&_table]:my-0 [&_pre]:bg-neutral-100 [&_pre]:text-neutral-800 [&_:not(pre)>code]:rounded [&_:not(pre)>code]:bg-neutral-100 [&_:not(pre)>code]:px-1 [&_:not(pre)>code]:py-0.5 [&_blockquote_p]:before:content-none [&_blockquote_p]:after:content-none [&_code]:before:content-none [&_code]:after:content-none" class="prose max-w-none leading-5 text-neutral-700 [&_:not(pre)>code]:rounded [&_:not(pre)>code]:bg-neutral-100 [&_:not(pre)>code]:px-1 [&_:not(pre)>code]:py-0.5 [&_blockquote_img]:mx-0 [&_blockquote_img]:w-full [&_blockquote_img]:max-w-full [&_blockquote_img]:rounded [&_blockquote_p]:before:content-none [&_blockquote_p]:after:content-none [&_code]:before:content-none [&_code]:after:content-none [&_img]:my-5 [&_p]:my-3 [&_pre]:bg-neutral-100 [&_pre]:text-neutral-800 [&_table]:my-0"
> >
{@render renderBlocks(blocks)} {@render renderBlocks(blocks)}
</div> </div>

View file

@ -61,7 +61,7 @@
aria-haspopup="menu" aria-haspopup="menu"
aria-expanded={open} aria-expanded={open}
aria-label="Sort discussions, current: {currentLabel}" aria-label="Sort discussions, current: {currentLabel}"
class="flex items-center gap-2 rounded bg-neutral-800 px-4 py-1.5 md:text-sm font-medium text-white hover:bg-neutral-700" class="flex items-center gap-2 rounded bg-neutral-800 px-4 py-1.5 font-medium text-white hover:bg-neutral-700 md:text-sm"
> >
<svg <svg
class="h-4 w-4" class="h-4 w-4"

View file

@ -3,6 +3,6 @@
let { label }: Props = $props(); let { label }: Props = $props();
</script> </script>
<span class="rounded-lg bg-accent px-2.5 py-0 text-sm text-white"> <span class="bg-accent rounded-lg px-2.5 py-0 text-sm text-white">
{label} {label}
</span> </span>

View file

@ -39,8 +39,8 @@
class="flex items-center gap-3 border-b border-neutral-100 py-4 hover:bg-neutral-50 sm:gap-6" class="flex items-center gap-3 border-b border-neutral-100 py-4 hover:bg-neutral-50 sm:gap-6"
> >
<!-- Col 1: title + byline --> <!-- Col 1: title + byline -->
<div class="flex-1 min-w-0"> <div class="min-w-0 flex-1">
<div class="text-lg mb-1.5 text-neutral-900 leading-5">{thread.title}</div> <div class="mb-1.5 text-lg leading-5 text-neutral-900">{thread.title}</div>
<div class="flex items-center gap-1.5 text-sm text-neutral-500"> <div class="flex items-center gap-1.5 text-sm text-neutral-500">
<span>by</span> <span>by</span>
{@render avatar(thread.author, "h-5 w-5 rounded-full object-cover")} {@render avatar(thread.author, "h-5 w-5 rounded-full object-cover")}

View file

@ -141,7 +141,7 @@
</script> </script>
<div <div
class="sticky self-start flex-shrink-0 flex-col items-end select-none pt-1 max-md:hidden {isOverflowing class="sticky flex-shrink-0 flex-col items-end self-start pt-1 select-none max-md:hidden {isOverflowing
? 'flex' ? 'flex'
: 'hidden'}" : 'hidden'}"
style="top: calc({topOffset}px - 1.5rem); height: 50vh; width: 72px;" style="top: calc({topOffset}px - 1.5rem); height: 50vh; width: 72px;"
@ -149,7 +149,7 @@
> >
<!-- First post date --> <!-- First post date -->
<div <div
class="text-xs text-neutral-300 mb-1 text-right leading-tight whitespace-pre" class="mb-1 text-right text-xs leading-tight whitespace-pre text-neutral-300"
> >
{firstPost ? formatDate(firstPost.createdAt) : ""} {firstPost ? formatDate(firstPost.createdAt) : ""}
</div> </div>
@ -157,7 +157,7 @@
<!-- Track area --> <!-- Track area -->
<div <div
bind:this={trackEl} bind:this={trackEl}
class="relative flex-1 w-full cursor-pointer" class="relative w-full flex-1 cursor-pointer"
onclick={onTrackClick} onclick={onTrackClick}
role="presentation" role="presentation"
> >
@ -169,7 +169,7 @@
<!-- Thumb --> <!-- Thumb -->
<div <div
class="absolute rounded-full bg-brand hover:bg-neutral-600 transition-colors cursor-grab active:cursor-grabbing touch-none" class="bg-brand absolute cursor-grab touch-none rounded-full transition-colors hover:bg-neutral-600 active:cursor-grabbing"
style="width: 4px; right: 3px; top: {thumbTop}px; height: {thumbHeight}px;" style="width: 4px; right: 3px; top: {thumbTop}px; height: {thumbHeight}px;"
onpointerdown={onThumbPointerDown} onpointerdown={onThumbPointerDown}
onpointermove={onThumbPointerMove} onpointermove={onThumbPointerMove}
@ -180,7 +180,7 @@
<!-- Current date label --> <!-- Current date label -->
<div <div
class="absolute text-xs text-neutral-400 whitespace-nowrap pointer-events-none leading-tight" class="pointer-events-none absolute text-xs leading-tight whitespace-nowrap text-neutral-400"
style="right: 18px; top: {thumbTop + style="right: 18px; top: {thumbTop +
thumbHeight / 2}px; transform: translateY(-50%);" thumbHeight / 2}px; transform: translateY(-50%);"
> >
@ -190,7 +190,7 @@
<!-- Last post date --> <!-- Last post date -->
<div <div
class="text-xs text-neutral-300 mt-1 text-right leading-tight whitespace-pre" class="mt-1 text-right text-xs leading-tight whitespace-pre text-neutral-300"
> >
{lastPost ? formatDate(lastPost.createdAt) : ""} {lastPost ? formatDate(lastPost.createdAt) : ""}
</div> </div>

View file

@ -104,7 +104,7 @@
> >
<Navbar onMenuToggle={() => (menuOpen = true)} /> <Navbar onMenuToggle={() => (menuOpen = true)} />
<div <div
class="relative md:flex md:flex-1 md:overflow-hidden md:gap-5 {mobileView === class="relative md:flex md:flex-1 md:gap-5 md:overflow-hidden {mobileView ===
'chat' 'chat'
? 'flex flex-1 overflow-hidden' ? 'flex flex-1 overflow-hidden'
: ''}" : ''}"
@ -120,7 +120,7 @@
it first and it reappears once the top is reached. Desktop only. --> it first and it reappears once the top is reached. Desktop only. -->
<div class="hidden md:block md:h-6" aria-hidden="true"></div> <div class="hidden md:block md:h-6" aria-hidden="true"></div>
<div <div
class="min-h-[calc(100dvh_-_4rem)] bg-white px-6 pt-4 md:pt-8 pb-20 shadow-lg md:min-h-full md:rounded-t-xl md:px-10 md:pt-6" class="min-h-[calc(100dvh_-_4rem)] bg-white px-6 pt-4 pb-20 shadow-lg md:min-h-full md:rounded-t-xl md:px-10 md:pt-6 md:pt-8"
> >
{@render children()} {@render children()}
</div> </div>
@ -136,7 +136,7 @@
<!-- Own panel (40%) so the gray gutter matches the main↔chat gap. <!-- Own panel (40%) so the gray gutter matches the main↔chat gap.
Hidden on mobile — it's supplementary to the main column. --> Hidden on mobile — it's supplementary to the main column. -->
<div <div
class="no-scrollbar hidden min-w-0 md:block md:flex-[2] md:min-h-0 md:overflow-y-auto" class="no-scrollbar hidden min-w-0 md:block md:min-h-0 md:flex-[2] md:overflow-y-auto"
> >
<div class="hidden md:block md:h-6" aria-hidden="true"></div> <div class="hidden md:block md:h-6" aria-hidden="true"></div>
<div <div

View file

@ -62,7 +62,7 @@
{#if partial} {#if partial}
<hr class="text-neutral-300" /> <hr class="text-neutral-300" />
{:else} {:else}
<h1 class="py-2 text-[1.65rem] text-brand">Forum rooms</h1> <h1 class="text-brand py-2 text-[1.65rem]">Forum rooms</h1>
{/if} {/if}
{#if groupsStore.list.length === 0} {#if groupsStore.list.length === 0}
@ -82,11 +82,11 @@
class="group flex items-start justify-between gap-4 py-5" class="group flex items-start justify-between gap-4 py-5"
> >
<div class="min-w-0"> <div class="min-w-0">
<h2 class="text-2xl text-neutral-800 group-hover:text-brand"> <h2 class="group-hover:text-brand text-2xl text-neutral-800">
{room.name} {room.name}
</h2> </h2>
{#if room.about} {#if room.about}
<p class="mt-1 text-neutral-600 leading-5">{room.about}</p> <p class="mt-1 leading-5 text-neutral-600">{room.about}</p>
{/if} {/if}
{#if admin} {#if admin}
<div class="mt-2 flex items-center gap-2 text-sm text-neutral-500"> <div class="mt-2 flex items-center gap-2 text-sm text-neutral-500">

View file

@ -72,7 +72,7 @@
</svelte:head> </svelte:head>
<div class="mx-auto max-w-6xl"> <div class="mx-auto max-w-6xl">
<h1 class="py-2 text-[1.65rem] text-brand">Contacts</h1> <h1 class="text-brand py-2 text-[1.65rem]">Contacts</h1>
{#if partial} {#if partial}
<div class="mb-8"> <div class="mb-8">
@ -120,7 +120,7 @@
href="https://njump.me/{c.npub}" href="https://njump.me/{c.npub}"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="shrink-0 text-sm text-brand hover:underline" class="text-brand shrink-0 text-sm hover:underline"
> >
View profile ↗ View profile ↗
</a> </a>
@ -139,7 +139,7 @@
href={websiteHref(c.entry.website)} href={websiteHref(c.entry.website)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="truncate text-brand hover:underline" class="text-brand truncate hover:underline"
> >
{websiteLabel(c.entry.website)} {websiteLabel(c.entry.website)}
</a> </a>

View file

@ -1,6 +1,6 @@
@import "tailwindcss"; @import "tailwindcss";
@plugin '@tailwindcss/forms'; @plugin "@tailwindcss/forms";
@plugin '@tailwindcss/typography'; @plugin "@tailwindcss/typography";
html { html {
font-size: 16px; font-size: 16px;

View file

@ -31,7 +31,7 @@
<div class="mx-auto max-w-6xl"> <div class="mx-auto max-w-6xl">
<a <a
href="/" href="/"
class="mb-1 inline-flex items-center gap-1 text-sm text-neutral-400 hover:text-brand" class="hover:text-brand mb-1 inline-flex items-center gap-1 text-sm text-neutral-400"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View file

@ -264,11 +264,11 @@
<img <img
src={author.picture} src={author.picture}
alt="" alt=""
class="w-12 h-12 rounded-full object-cover" class="h-12 w-12 rounded-full object-cover"
/> />
{:else} {:else}
<span <span
class="w-12 h-12 flex items-center justify-center rounded-full bg-neutral-200 text-lg font-semibold text-neutral-500" class="flex h-12 w-12 items-center justify-center rounded-full bg-neutral-200 text-lg font-semibold text-neutral-500"
> >
{author.name[0].toUpperCase()} {author.name[0].toUpperCase()}
</span> </span>
@ -281,11 +281,11 @@
bindEl: (el: HTMLElement | null) => void, bindEl: (el: HTMLElement | null) => void,
)} )}
{@const author = resolveAuthor(p.pubkey, profiles)} {@const author = resolveAuthor(p.pubkey, profiles)}
<div use:bindEl class="md:flex md:gap-6 md:items-start"> <div use:bindEl class="md:flex md:items-start md:gap-6">
<div class="hidden flex-shrink-0 md:block"> <div class="hidden flex-shrink-0 md:block">
{@render avatar(author)} {@render avatar(author)}
</div> </div>
<div class="flex-1 min-w-0"> <div class="min-w-0 flex-1">
<div <div
class="mb-4 flex items-center justify-between md:mb-3 md:items-baseline" class="mb-4 flex items-center justify-between md:mb-3 md:items-baseline"
> >
@ -321,7 +321,7 @@
{#if openMenuId === p.id} {#if openMenuId === p.id}
<div <div
role="menu" role="menu"
class="absolute right-0 top-7 z-20 w-36 rounded-lg border border-neutral-100 bg-white py-1 text-sm shadow-lg" class="absolute top-7 right-0 z-20 w-36 rounded-lg border border-neutral-100 bg-white py-1 text-sm shadow-lg"
> >
<button <button
role="menuitem" role="menuitem"
@ -344,11 +344,11 @@
<PostContent content={p.content} {profiles} {threadEventAuthors} /> <PostContent content={p.content} {profiles} {threadEventAuthors} />
</div> </div>
{#if auth.user} {#if auth.user}
<div class="flex items-center justify-start mt-6"> <div class="mt-6 flex items-center justify-start">
<button <button
onclick={() => quotePost(p)} onclick={() => quotePost(p)}
disabled={!auth.user} disabled={!auth.user}
class="flex items-center gap-1.5 text-neutral-300 cursor-pointer hover:text-brand transition-colors disabled:opacity-50 disabled:cursor-not-allowed" class="hover:text-brand flex cursor-pointer items-center gap-1.5 text-neutral-300 transition-colors disabled:cursor-not-allowed disabled:opacity-50"
> >
<span>Quote post</span> <span>Quote post</span>
<svg <svg
@ -372,14 +372,14 @@
{/snippet} {/snippet}
{#if detail} {#if detail}
<div class="flex gap-6 items-start"> <div class="flex items-start gap-6">
<div class="flex-1 min-w-0 {!scrubberVisible ? 'md:pr-18' : ''}"> <div class="min-w-0 flex-1 {!scrubberVisible ? 'md:pr-18' : ''}">
<div <div
class="relative z-10 bg-white pb-1 md:sticky md:-top-6 md:-mx-10 md:px-10 md:pt-6 md:-mt-6" class="relative z-10 bg-white pb-1 md:sticky md:-top-6 md:-mx-10 md:-mt-6 md:px-10 md:pt-6"
> >
<a <a
href={MODE === "full" ? `/room/${detail.groupId}` : "/"} href={MODE === "full" ? `/room/${detail.groupId}` : "/"}
class="mb-1 inline-flex items-center gap-1 text-sm text-neutral-400 hover:text-brand" class="hover:text-brand mb-1 inline-flex items-center gap-1 text-sm text-neutral-400"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -398,10 +398,10 @@
</svg> </svg>
Discussions Discussions
</a> </a>
<h1 class="text-[1.65rem] text-brand leading-7">{detail.title}</h1> <h1 class="text-brand text-[1.65rem] leading-7">{detail.title}</h1>
{#if isScrolled} {#if isScrolled}
<div <div
class="absolute left-0 right-0 h-8 pointer-events-none transition-opacity duration-200" class="pointer-events-none absolute right-0 left-0 h-8 transition-opacity duration-200"
style="top: 100%; background: linear-gradient(to bottom, white, transparent);" style="top: 100%; background: linear-gradient(to bottom, white, transparent);"
></div> ></div>
{/if} {/if}
@ -433,7 +433,7 @@
{#if auth.user} {#if auth.user}
{#if replyError} {#if replyError}
<div <div
class="mb-4 rounded bg-red-50 px-4 py-3 text-sm text-red-700 border border-red-200" class="mb-4 rounded border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700"
> >
{replyError} {replyError}
</div> </div>
@ -452,13 +452,13 @@
<button <button
onclick={submitReply} onclick={submitReply}
disabled={replying || !replyContent.trim()} disabled={replying || !replyContent.trim()}
class="rounded bg-brand px-6 py-1.5 font-medium text-white hover:bg-brand-hover disabled:opacity-50 disabled:cursor-not-allowed" class="bg-brand hover:bg-brand-hover rounded px-6 py-1.5 font-medium text-white disabled:cursor-not-allowed disabled:opacity-50"
> >
{replying ? "Posting…" : "post reply"} {replying ? "Posting…" : "post reply"}
</button> </button>
</div> </div>
{:else} {:else}
<p class=" text-neutral-500 text-center"> <p class=" text-center text-neutral-500">
To participate and reply, please To participate and reply, please
<button onclick={openLogin} class="text-brand hover:underline" <button onclick={openLogin} class="text-brand hover:underline"
>login now</button >login now</button
@ -485,7 +485,7 @@
onmousedown={(e) => e.preventDefault()} onmousedown={(e) => e.preventDefault()}
onclick={quoteFromSelection} onclick={quoteFromSelection}
style="top: {selectionTarget.top}px; left: {selectionTarget.left}px;" style="top: {selectionTarget.top}px; left: {selectionTarget.left}px;"
class="fixed -translate-x-1/2 -translate-y-full z-50 rounded bg-neutral-900 px-3 py-1 text-xs font-medium text-white shadow-md hover:bg-neutral-700" class="fixed z-50 -translate-x-1/2 -translate-y-full rounded bg-neutral-900 px-3 py-1 text-xs font-medium text-white shadow-md hover:bg-neutral-700"
> >
Quote Quote
</button> </button>