feat(embed): Add support for dark mode (#6912)
* feat(embed): Support dark mode (wip) * finishing up the implementation * fix tailwind color selector * tweak design * refactor: unify types * fix * fix english grammar * refactor: unify types * tweak design * remove the customization part
This commit is contained in:
@@ -20,6 +20,7 @@ window.addEventListener('message', event => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const id = (event.data as {id: string}).id
|
const id = (event.data as {id: string}).id
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return
|
return
|
||||||
@@ -33,6 +34,7 @@ window.addEventListener('message', event => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const height = (event.data as {height: number}).height
|
const height = (event.data as {height: number}).height
|
||||||
if (height) {
|
if (height) {
|
||||||
embed.style.height = `${height}px`
|
embed.style.height = `${height}px`
|
||||||
@@ -47,7 +49,7 @@ window.addEventListener('message', event => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function scan(node = document) {
|
function scan(node = document) {
|
||||||
const embeds = node.querySelectorAll('[data-bluesky-uri]')
|
const embeds = node.querySelectorAll<HTMLIFrameElement>('[data-bluesky-uri]')
|
||||||
|
|
||||||
for (let i = 0; i < embeds.length; i++) {
|
for (let i = 0; i < embeds.length; i++) {
|
||||||
const id = String(Math.random()).slice(2)
|
const id = String(Math.random()).slice(2)
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export function applyTheme(theme: 'light' | 'dark') {
|
||||||
|
document.documentElement.classList.remove('light', 'dark')
|
||||||
|
document.documentElement.classList.add(theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initColorMode() {
|
||||||
|
applyTheme(
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
? 'dark'
|
||||||
|
: 'light',
|
||||||
|
)
|
||||||
|
window
|
||||||
|
.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
.addEventListener('change', mql => {
|
||||||
|
applyTheme(mql.matches ? 'dark' : 'light')
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ export function Container({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="w-full bg-white hover:bg-neutral-50 relative transition-colors max-w-[600px] min-w-[300px] flex border rounded-xl"
|
className="w-full bg-white text-black hover:bg-neutral-50 dark:bg-dimmedBg dark:hover:bg-dimmedBgLighten relative transition-colors max-w-[600px] min-w-[300px] flex border dark:border-slate-600 dark:text-slate-200 rounded-xl"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (ref.current && href) {
|
if (ref.current && href) {
|
||||||
// forwardRef requires preact/compat - let's keep it simple
|
// forwardRef requires preact/compat - let's keep it simple
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ export function Embed({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={`/profile/${record.author.did}/post/${getRkey(record)}`}
|
href={`/profile/${record.author.did}/post/${getRkey(record)}`}
|
||||||
className="transition-colors hover:bg-neutral-100 border rounded-lg p-2 gap-1.5 w-full flex flex-col">
|
className="transition-colors hover:bg-neutral-100 dark:hover:bg-slate-700 border dark:border-slate-600 rounded-lg p-2 gap-1.5 w-full flex flex-col">
|
||||||
<div className="flex gap-1.5 items-center">
|
<div className="flex gap-1.5 items-center">
|
||||||
<div className="w-4 h-4 overflow-hidden rounded-full bg-neutral-300 shrink-0">
|
<div className="w-4 h-4 overflow-hidden rounded-full bg-neutral-300 dark:bg-slate-700 shrink-0">
|
||||||
<img
|
<img
|
||||||
src={record.author.avatar}
|
src={record.author.avatar}
|
||||||
style={isAuthorLabeled ? {filter: 'blur(1.5px)'} : undefined}
|
style={isAuthorLabeled ? {filter: 'blur(1.5px)'} : undefined}
|
||||||
@@ -88,7 +88,7 @@ export function Embed({
|
|||||||
</div>
|
</div>
|
||||||
<p className="line-clamp-1 text-sm">
|
<p className="line-clamp-1 text-sm">
|
||||||
<span className="font-bold">{record.author.displayName}</span>
|
<span className="font-bold">{record.author.displayName}</span>
|
||||||
<span className="text-textLight ml-1">
|
<span className="text-textLight dark:text-textDimmed ml-1">
|
||||||
@{record.author.handle}
|
@{record.author.handle}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
@@ -209,7 +209,7 @@ function Info({children}: {children: ComponentChildren}) {
|
|||||||
return (
|
return (
|
||||||
<div className="w-full rounded-lg border py-2 px-2.5 flex-row flex gap-2 bg-neutral-50">
|
<div className="w-full rounded-lg border py-2 px-2.5 flex-row flex gap-2 bg-neutral-50">
|
||||||
<img src={infoIcon} className="w-4 h-4 shrink-0 mt-0.5" />
|
<img src={infoIcon} className="w-4 h-4 shrink-0 mt-0.5" />
|
||||||
<p className="text-sm text-textLight">{children}</p>
|
<p className="text-sm text-textLight dark:text-textDimmed">{children}</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -308,7 +308,7 @@ function ExternalEmbed({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={content.external.uri}
|
href={content.external.uri}
|
||||||
className="w-full rounded-lg overflow-hidden border flex flex-col items-stretch"
|
className="w-full rounded-lg overflow-hidden border dark:border-slate-600 flex flex-col items-stretch"
|
||||||
disableTracking>
|
disableTracking>
|
||||||
{content.external.thumb && (
|
{content.external.thumb && (
|
||||||
<img
|
<img
|
||||||
@@ -317,11 +317,11 @@ function ExternalEmbed({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="py-3 px-4">
|
<div className="py-3 px-4">
|
||||||
<p className="text-sm text-textLight line-clamp-1">
|
<p className="text-sm text-textLight dark:text-textDimmed line-clamp-1">
|
||||||
{toNiceDomain(content.external.uri)}
|
{toNiceDomain(content.external.uri)}
|
||||||
</p>
|
</p>
|
||||||
<p className="font-semibold line-clamp-3">{content.external.title}</p>
|
<p className="font-semibold line-clamp-3">{content.external.title}</p>
|
||||||
<p className="text-sm text-textLight line-clamp-2 mt-0.5">
|
<p className="text-sm text-textLight dark:text-textDimmed line-clamp-2 mt-0.5">
|
||||||
{content.external.description}
|
{content.external.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -345,23 +345,29 @@ function GenericWithImageEmbed({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={href}
|
href={href}
|
||||||
className="w-full rounded-lg border py-2 px-3 flex flex-col gap-2">
|
className="w-full rounded-lg border dark:border-slate-600 py-2 px-3 flex flex-col gap-2">
|
||||||
<div className="flex gap-2.5 items-center">
|
<div className="flex gap-2.5 items-center">
|
||||||
{image ? (
|
{image ? (
|
||||||
<img
|
<img
|
||||||
src={image}
|
src={image}
|
||||||
alt={title}
|
alt={title}
|
||||||
className="w-8 h-8 rounded-md bg-neutral-300 shrink-0"
|
className="w-8 h-8 rounded-md bg-neutral-300 dark:bg-slate-700 shrink-0"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-8 h-8 rounded-md bg-brand shrink-0" />
|
<div className="w-8 h-8 rounded-md bg-brand shrink-0" />
|
||||||
)}
|
)}
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<p className="font-bold text-sm">{title}</p>
|
<p className="font-bold text-sm">{title}</p>
|
||||||
<p className="text-textLight text-sm">{subtitle}</p>
|
<p className="text-textLight dark:text-textDimmed text-sm">
|
||||||
|
{subtitle}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{description && <p className="text-textLight text-sm">{description}</p>}
|
{description && (
|
||||||
|
<p className="text-textLight dark:text-textDimmed text-sm">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -406,7 +412,7 @@ function StarterPackEmbed({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={starterPackHref}
|
href={starterPackHref}
|
||||||
className="w-full rounded-lg overflow-hidden border flex flex-col items-stretch">
|
className="w-full rounded-lg overflow-hidden border dark:border-slate-600 flex flex-col items-stretch">
|
||||||
<img src={imageUri} className="aspect-[1.91/1] object-cover" />
|
<img src={imageUri} className="aspect-[1.91/1] object-cover" />
|
||||||
<div className="py-3 px-4">
|
<div className="py-3 px-4">
|
||||||
<div className="flex space-x-2 items-center">
|
<div className="flex space-x-2 items-center">
|
||||||
@@ -415,7 +421,7 @@ function StarterPackEmbed({
|
|||||||
<p className="font-semibold leading-[21px]">
|
<p className="font-semibold leading-[21px]">
|
||||||
{content.record.name}
|
{content.record.name}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-textLight line-clamp-2 leading-[18px]">
|
<p className="text-sm text-textLight dark:text-textDimmed line-clamp-2 leading-[18px]">
|
||||||
Starter pack by{' '}
|
Starter pack by{' '}
|
||||||
{content.creator.displayName || `@${content.creator.handle}`}
|
{content.creator.displayName || `@${content.creator.handle}`}
|
||||||
</p>
|
</p>
|
||||||
@@ -425,7 +431,7 @@ function StarterPackEmbed({
|
|||||||
<p className="text-sm mt-1">{content.record.description}</p>
|
<p className="text-sm mt-1">{content.record.description}</p>
|
||||||
)}
|
)}
|
||||||
{!!content.joinedAllTimeCount && content.joinedAllTimeCount > 50 && (
|
{!!content.joinedAllTimeCount && content.joinedAllTimeCount > 50 && (
|
||||||
<p className="text-sm font-semibold text-textLight mt-1">
|
<p className="text-sm font-semibold text-textLight dark:text-textDimmed mt-1">
|
||||||
{content.joinedAllTimeCount} users have joined!
|
{content.joinedAllTimeCount} users have joined!
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function Post({thread}: Props) {
|
|||||||
<div className="flex-1 flex-col flex gap-2" lang={record?.langs?.[0]}>
|
<div className="flex-1 flex-col flex gap-2" lang={record?.langs?.[0]}>
|
||||||
<div className="flex gap-2.5 items-center cursor-pointer">
|
<div className="flex gap-2.5 items-center cursor-pointer">
|
||||||
<Link href={`/profile/${post.author.did}`} className="rounded-full">
|
<Link href={`/profile/${post.author.did}`} className="rounded-full">
|
||||||
<div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-300 shrink-0">
|
<div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-300 dark:bg-slate-700 shrink-0">
|
||||||
<img
|
<img
|
||||||
src={post.author.avatar}
|
src={post.author.avatar}
|
||||||
style={isAuthorLabeled ? {filter: 'blur(2.5px)'} : undefined}
|
style={isAuthorLabeled ? {filter: 'blur(2.5px)'} : undefined}
|
||||||
@@ -53,7 +53,7 @@ export function Post({thread}: Props) {
|
|||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href={`/profile/${post.author.did}`}
|
href={`/profile/${post.author.did}`}
|
||||||
className="text-[15px] text-textLight hover:underline line-clamp-1">
|
className="text-[15px] text-textLight dark:text-textDimmed hover:underline line-clamp-1">
|
||||||
<p>@{post.author.handle}</p>
|
<p>@{post.author.handle}</p>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,15 +69,15 @@ export function Post({thread}: Props) {
|
|||||||
<Link href={href}>
|
<Link href={href}>
|
||||||
<time
|
<time
|
||||||
datetime={new Date(post.indexedAt).toISOString()}
|
datetime={new Date(post.indexedAt).toISOString()}
|
||||||
className="text-textLight mt-1 text-sm hover:underline">
|
className="text-textLight dark:text-textDimmed mt-1 text-sm hover:underline">
|
||||||
{niceDate(post.indexedAt)}
|
{niceDate(post.indexedAt)}
|
||||||
</time>
|
</time>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="border-t w-full pt-2.5 flex items-center gap-5 text-sm cursor-pointer">
|
<div className="border-t dark:border-slate-600 w-full pt-2.5 flex items-center gap-5 text-sm cursor-pointer">
|
||||||
{!!post.likeCount && (
|
{!!post.likeCount && (
|
||||||
<div className="flex items-center gap-2 cursor-pointer">
|
<div className="flex items-center gap-2 cursor-pointer">
|
||||||
<img src={likeIcon} className="w-5 h-5" />
|
<img src={likeIcon} className="w-5 h-5" />
|
||||||
<p className="font-bold text-neutral-500 mb-px">
|
<p className="font-bold text-neutral-500 dark:text-neutral-300 mb-px">
|
||||||
{prettyNumber(post.likeCount)}
|
{prettyNumber(post.likeCount)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,17 +85,19 @@ export function Post({thread}: Props) {
|
|||||||
{!!post.repostCount && (
|
{!!post.repostCount && (
|
||||||
<div className="flex items-center gap-2 cursor-pointer">
|
<div className="flex items-center gap-2 cursor-pointer">
|
||||||
<img src={repostIcon} className="w-5 h-5" />
|
<img src={repostIcon} className="w-5 h-5" />
|
||||||
<p className="font-bold text-neutral-500 mb-px">
|
<p className="font-bold text-neutral-500 dark:text-neutral-300 mb-px">
|
||||||
{prettyNumber(post.repostCount)}
|
{prettyNumber(post.repostCount)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center gap-2 cursor-pointer">
|
<div className="flex items-center gap-2 cursor-pointer">
|
||||||
<img src={replyIcon} className="w-5 h-5" />
|
<img src={replyIcon} className="w-5 h-5" />
|
||||||
<p className="font-bold text-neutral-500 mb-px">Reply</p>
|
<p className="font-bold text-neutral-500 dark:text-neutral-300 mb-px">
|
||||||
|
Reply
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
<p className="cursor-pointer text-brand font-bold hover:underline hidden min-[450px]:inline">
|
<p className="cursor-pointer text-brand dark:text-brandLighten font-bold hover:underline hidden min-[450px]:inline">
|
||||||
{post.replyCount
|
{post.replyCount
|
||||||
? `Read ${prettyNumber(post.replyCount)} ${
|
? `Read ${prettyNumber(post.replyCount)} ${
|
||||||
post.replyCount > 1 ? 'replies' : 'reply'
|
post.replyCount > 1 ? 'replies' : 'reply'
|
||||||
|
|||||||
@@ -5,3 +5,7 @@
|
|||||||
.break-word {
|
.break-word {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
color-scheme: light dark;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {useEffect, useMemo, useRef, useState} from 'preact/hooks'
|
|||||||
|
|
||||||
import arrowBottom from '../../assets/arrowBottom_stroke2_corner0_rounded.svg'
|
import arrowBottom from '../../assets/arrowBottom_stroke2_corner0_rounded.svg'
|
||||||
import logo from '../../assets/logo.svg'
|
import logo from '../../assets/logo.svg'
|
||||||
|
import {initColorMode} from '../color-mode'
|
||||||
import {Container} from '../components/container'
|
import {Container} from '../components/container'
|
||||||
import {Link} from '../components/link'
|
import {Link} from '../components/link'
|
||||||
import {Post} from '../components/post'
|
import {Post} from '../components/post'
|
||||||
@@ -21,6 +22,8 @@ export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js`
|
|||||||
const root = document.getElementById('app')
|
const root = document.getElementById('app')
|
||||||
if (!root) throw new Error('No root element')
|
if (!root) throw new Error('No root element')
|
||||||
|
|
||||||
|
initColorMode()
|
||||||
|
|
||||||
const agent = new BskyAgent({
|
const agent = new BskyAgent({
|
||||||
service: 'https://public.api.bsky.app',
|
service: 'https://public.api.bsky.app',
|
||||||
})
|
})
|
||||||
@@ -108,7 +111,7 @@ function LandingPage() {
|
|||||||
}, [uri])
|
}, [uri])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="w-full min-h-screen flex flex-col items-center gap-8 py-14 px-4 md:pt-32">
|
<main className="w-full min-h-screen flex flex-col items-center gap-8 py-14 px-4 md:pt-32 dark:bg-dimmedBgDarken dark:text-slate-200">
|
||||||
<Link
|
<Link
|
||||||
href="https://bsky.social/about"
|
href="https://bsky.social/about"
|
||||||
className="transition-transform hover:scale-110">
|
className="transition-transform hover:scale-110">
|
||||||
@@ -121,20 +124,22 @@ function LandingPage() {
|
|||||||
type="text"
|
type="text"
|
||||||
value={uri}
|
value={uri}
|
||||||
onInput={e => setUri(e.currentTarget.value)}
|
onInput={e => setUri(e.currentTarget.value)}
|
||||||
className="border rounded-lg py-3 w-full max-w-[600px] px-4"
|
className="border rounded-lg py-3 w-full max-w-[600px] px-4 dark:bg-dimmedBg dark:border-slate-500"
|
||||||
placeholder={DEFAULT_POST}
|
placeholder={DEFAULT_POST}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<img src={arrowBottom} className="w-6" />
|
<img src={arrowBottom} className="w-6 dark:invert" />
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Skeleton />
|
<div className="w-full max-w-[600px]">
|
||||||
|
<Skeleton />
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full max-w-[600px] gap-8 flex flex-col">
|
<div className="w-full max-w-[600px] gap-8 flex flex-col">
|
||||||
{!error && thread && uri && <Snippet thread={thread} />}
|
{!error && thread && uri && <Snippet thread={thread} />}
|
||||||
{!error && thread && <Post thread={thread} key={thread.post.uri} />}
|
{!error && thread && <Post thread={thread} key={thread.post.uri} />}
|
||||||
{error && (
|
{error && (
|
||||||
<div className="w-full border border-red-500 bg-red-50 px-4 py-3 rounded-lg">
|
<div className="w-full border border-red-500 bg-red-500/10 px-4 py-3 rounded-lg">
|
||||||
<p className="text-red-500 text-center">{error}</p>
|
<p className="text-red-500 text-center">{error}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -149,15 +154,15 @@ function Skeleton() {
|
|||||||
<Container>
|
<Container>
|
||||||
<div className="flex-1 flex-col flex gap-2 pb-8">
|
<div className="flex-1 flex-col flex gap-2 pb-8">
|
||||||
<div className="flex gap-2.5 items-center">
|
<div className="flex gap-2.5 items-center">
|
||||||
<div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-100 shrink-0 animate-pulse" />
|
<div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-100 dark:bg-slate-700 shrink-0 animate-pulse" />
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="bg-neutral-100 animate-pulse w-64 h-4 rounded" />
|
<div className="bg-neutral-100 dark:bg-slate-700 animate-pulse w-64 h-4 rounded" />
|
||||||
<div className="bg-neutral-100 animate-pulse w-32 h-3 mt-1 rounded" />
|
<div className="bg-neutral-100 dark:bg-slate-700 animate-pulse w-32 h-3 mt-1 rounded" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full h-4 mt-2 bg-neutral-100 rounded animate-pulse" />
|
<div className="w-full h-4 mt-2 bg-neutral-100 dark:bg-slate-700 rounded animate-pulse" />
|
||||||
<div className="w-5/6 h-4 bg-neutral-100 rounded animate-pulse" />
|
<div className="w-5/6 h-4 bg-neutral-100 dark:bg-slate-700 rounded animate-pulse" />
|
||||||
<div className="w-3/4 h-4 bg-neutral-100 rounded animate-pulse" />
|
<div className="w-3/4 h-4 bg-neutral-100 dark:bg-slate-700 rounded animate-pulse" />
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
@@ -220,7 +225,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
type="text"
|
type="text"
|
||||||
value={snippet}
|
value={snippet}
|
||||||
className="border rounded-lg py-3 w-full px-4"
|
className="border rounded-lg py-3 w-full px-4 dark:bg-dimmedBg dark:border-slate-500"
|
||||||
readOnly
|
readOnly
|
||||||
autoFocus
|
autoFocus
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
@@ -228,7 +233,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="rounded-lg bg-brand text-white color-white py-3 px-4 whitespace-nowrap min-w-28"
|
className="rounded-lg bg-brand text-white py-3 px-4 whitespace-nowrap min-w-28"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
ref.current?.focus()
|
ref.current?.focus()
|
||||||
ref.current?.select()
|
ref.current?.select()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {AppBskyFeedDefs, AtpAgent} from '@atproto/api'
|
|||||||
import {h, render} from 'preact'
|
import {h, render} from 'preact'
|
||||||
|
|
||||||
import logo from '../../assets/logo.svg'
|
import logo from '../../assets/logo.svg'
|
||||||
|
import {initColorMode} from '../color-mode'
|
||||||
import {Container} from '../components/container'
|
import {Container} from '../components/container'
|
||||||
import {Link} from '../components/link'
|
import {Link} from '../components/link'
|
||||||
import {Post} from '../components/post'
|
import {Post} from '../components/post'
|
||||||
@@ -21,6 +22,8 @@ if (!uri) {
|
|||||||
throw new Error('No uri in path')
|
throw new Error('No uri in path')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initColorMode()
|
||||||
|
|
||||||
agent
|
agent
|
||||||
.getPostThread({
|
.getPostThread({
|
||||||
uri,
|
uri,
|
||||||
@@ -55,13 +58,13 @@ function PwiOptOut({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
|
|||||||
<img src={logo} className="h-6" />
|
<img src={logo} className="h-6" />
|
||||||
</Link>
|
</Link>
|
||||||
<div className="w-full py-12 gap-4 flex flex-col items-center">
|
<div className="w-full py-12 gap-4 flex flex-col items-center">
|
||||||
<p className="max-w-80 text-center w-full text-textLight">
|
<p className="max-w-80 text-center w-full text-textLight dark:text-textDimmed">
|
||||||
The author of this post has requested their posts not be displayed on
|
The author of this post has requested their posts not be displayed on
|
||||||
external sites.
|
external sites.
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
href={href}
|
href={href}
|
||||||
className="max-w-80 rounded-lg bg-brand text-white color-white text-center py-1 px-4 w-full mx-auto">
|
className="max-w-80 rounded-lg bg-brand text-white text-center py-1 px-4 w-full mx-auto">
|
||||||
View on Bluesky
|
View on Bluesky
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,7 +80,7 @@ function ErrorMessage() {
|
|||||||
className="transition-transform hover:scale-110 absolute top-4 right-4">
|
className="transition-transform hover:scale-110 absolute top-4 right-4">
|
||||||
<img src={logo} className="h-6" />
|
<img src={logo} className="h-6" />
|
||||||
</Link>
|
</Link>
|
||||||
<p className="my-16 text-center w-full text-textLight">
|
<p className="my-16 text-center w-full text-textLight dark:text-textDimmed">
|
||||||
Post not found, it may have been deleted.
|
Post not found, it may have been deleted.
|
||||||
</p>
|
</p>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||||
|
darkMode: ['variant', [
|
||||||
|
'&:is(.dark *):not(:is(.dark .light *))',
|
||||||
|
]],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
brand: 'rgb(10,122,255)',
|
brand: 'rgb(10,122,255)',
|
||||||
|
brandLighten: 'rgb(32,139,254)',
|
||||||
textLight: 'rgb(66,87,108)',
|
textLight: 'rgb(66,87,108)',
|
||||||
|
textDimmed: 'rgb(174,187,201)',
|
||||||
|
dimmedBgLighten: 'rgb(30,41,54)',
|
||||||
|
dimmedBg: 'rgb(22,30,39)',
|
||||||
|
dimmedBgDarken: 'rgb(18,25,32)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES5",
|
"target": "ES5",
|
||||||
@@ -20,5 +19,5 @@
|
|||||||
"jsxFragmentFactory": "Fragment",
|
"jsxFragmentFactory": "Fragment",
|
||||||
"downlevelIteration": true
|
"downlevelIteration": true
|
||||||
},
|
},
|
||||||
"include": ["src", "vite.config.ts"]
|
"include": ["src", "snippet", "vite.config.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user