diff options
author | Samuel Newman <mozzius@protonmail.com> | 2025-03-05 17:24:59 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-05 09:24:59 -0800 |
commit | 2d854091b9684ab253a2c117509bf95609a975a1 (patch) | |
tree | 965f049c7d8e13e61bd1ce05737113340fb3d991 /bskyembed | |
parent | 01a51c327505bc84f5755be82f15a855234a2750 (diff) | |
download | voidsky-2d854091b9684ab253a2c117509bf95609a975a1.tar.zst |
enhance(embed): add ability to pin color mode (#7186)
* enhance(embed): add ability to pin color mode * fix: Move color mode dropdown to the root section * auto -> system * style tweaks * default to light theme * try and fix eslint * fix dropdown styles on other browsers * rm unnecessary eslintrc change * more explicit color mode select * make light explicit --------- Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
Diffstat (limited to 'bskyembed')
-rw-r--r-- | bskyembed/snippet/embed.ts | 1 | ||||
-rw-r--r-- | bskyembed/src/color-mode.ts | 8 | ||||
-rw-r--r-- | bskyembed/src/index.css | 11 | ||||
-rw-r--r-- | bskyembed/src/screens/landing.tsx | 77 | ||||
-rw-r--r-- | bskyembed/src/screens/post.tsx | 20 |
5 files changed, 95 insertions, 22 deletions
diff --git a/bskyembed/snippet/embed.ts b/bskyembed/snippet/embed.ts index 3c1b14b95..7de7af1fe 100644 --- a/bskyembed/snippet/embed.ts +++ b/bskyembed/snippet/embed.ts @@ -68,6 +68,7 @@ function scan(node = document) { if (ref_url.startsWith('http')) { searchParams.set('ref_url', encodeURIComponent(ref_url)) } + searchParams.set('colorMode', embed.dataset.blueskyColorMode || 'system') const iframe = document.createElement('iframe') iframe.setAttribute('data-bluesky-id', id) diff --git a/bskyembed/src/color-mode.ts b/bskyembed/src/color-mode.ts index 2b392c617..b34624e31 100644 --- a/bskyembed/src/color-mode.ts +++ b/bskyembed/src/color-mode.ts @@ -1,9 +1,15 @@ +export type ColorModeValues = 'system' | 'light' | 'dark' + +export function assertColorModeValues(value: string): value is ColorModeValues { + return ['system', 'light', 'dark'].includes(value) +} + export function applyTheme(theme: 'light' | 'dark') { document.documentElement.classList.remove('light', 'dark') document.documentElement.classList.add(theme) } -export function initColorMode() { +export function initSystemColorMode() { applyTheme( window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' diff --git a/bskyembed/src/index.css b/bskyembed/src/index.css index 289e34cf0..efd9f4a4e 100644 --- a/bskyembed/src/index.css +++ b/bskyembed/src/index.css @@ -9,3 +9,14 @@ :root { color-scheme: light dark; } + +select { + background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='14px' width='14px' fill='none' viewBox='0 0 24 24'><path fill='black' fill-rule='evenodd' d='M3.293 8.293a1 1 0 0 1 1.414 0L12 15.586l7.293-7.293a1 1 0 1 1 1.414 1.414l-8 8a1 1 0 0 1-1.414 0l-8-8a1 1 0 0 1 0-1.414Z' clip-rule='evenodd'/></svg>"); + background-repeat: no-repeat; + background-position: calc(100% - 0.75rem) center; + padding-right: 2rem; + + @media (prefers-color-scheme: dark) { + background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='14px' width='14px' fill='none' viewBox='0 0 24 24'><path fill='white' fill-rule='evenodd' d='M3.293 8.293a1 1 0 0 1 1.414 0L12 15.586l7.293-7.293a1 1 0 1 1 1.414 1.414l-8 8a1 1 0 0 1-1.414 0l-8-8a1 1 0 0 1 0-1.414Z' clip-rule='evenodd'/></svg>"); + } +} diff --git a/bskyembed/src/screens/landing.tsx b/bskyembed/src/screens/landing.tsx index a3448e90a..880b71337 100644 --- a/bskyembed/src/screens/landing.tsx +++ b/bskyembed/src/screens/landing.tsx @@ -1,12 +1,16 @@ import '../index.css' -import {AppBskyFeedDefs, AppBskyFeedPost, AtUri, BskyAgent} from '@atproto/api' +import {AppBskyFeedDefs, AppBskyFeedPost, AtpAgent, AtUri} from '@atproto/api' import {h, render} from 'preact' import {useEffect, useMemo, useRef, useState} from 'preact/hooks' import arrowBottom from '../../assets/arrowBottom_stroke2_corner0_rounded.svg' import logo from '../../assets/logo.svg' -import {initColorMode} from '../color-mode' +import { + assertColorModeValues, + ColorModeValues, + initSystemColorMode, +} from '../color-mode' import {Container} from '../components/container' import {Link} from '../components/link' import {Post} from '../components/post' @@ -22,9 +26,9 @@ export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js` const root = document.getElementById('app') if (!root) throw new Error('No root element') -initColorMode() +initSystemColorMode() -const agent = new BskyAgent({ +const agent = new AtpAgent({ service: 'https://public.api.bsky.app', }) @@ -32,6 +36,7 @@ render(<LandingPage />, root) function LandingPage() { const [uri, setUri] = useState('') + const [colorMode, setColorMode] = useState<ColorModeValues>('system') const [error, setError] = useState<string | null>(null) const [loading, setLoading] = useState(false) const [thread, setThread] = useState<AppBskyFeedDefs.ThreadViewPost | null>( @@ -120,24 +125,50 @@ function LandingPage() { <h1 className="text-4xl font-bold text-center">Embed a Bluesky Post</h1> - <input - type="text" - value={uri} - onInput={e => setUri(e.currentTarget.value)} - className="border rounded-lg py-3 w-full max-w-[600px] px-4 dark:bg-dimmedBg dark:border-slate-500" - placeholder={DEFAULT_POST} - /> + <div className="flex flex-col w-full max-w-[600px] gap-6"> + <input + type="text" + value={uri} + onInput={e => setUri(e.currentTarget.value)} + className="border rounded-lg py-3 px-4 dark:bg-dimmedBg dark:border-slate-500" + placeholder={DEFAULT_POST} + /> + + <div className="flex flex-col gap-1.5"> + <label className="text-sm font-medium" for="colorModeSelect"> + Theme + </label> + <select + value={colorMode} + onChange={e => { + const value = e.currentTarget.value + if (assertColorModeValues(value)) { + setColorMode(value) + } + }} + id="colorModeSelect" + className="appearance-none bg-white border w-full rounded-lg text-sm px-3 py-2 dark:bg-dimmedBg dark:border-slate-500"> + <option value="system">System</option> + <option value="light">Light</option> + <option value="dark">Dark</option> + </select> + </div> + </div> <img src={arrowBottom} className="w-6 dark:invert" /> {loading ? ( - <div className="w-full max-w-[600px]"> + <div className={`${colorMode} w-full max-w-[600px]`}> <Skeleton /> </div> ) : ( <div className="w-full max-w-[600px] gap-8 flex flex-col"> - {!error && thread && uri && <Snippet thread={thread} />} - {!error && thread && <Post thread={thread} key={thread.post.uri} />} + {!error && thread && uri && ( + <Snippet thread={thread} colorMode={colorMode} /> + )} + <div className={colorMode}> + {!error && thread && <Post thread={thread} key={thread.post.uri} />} + </div> {error && ( <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> @@ -168,7 +199,13 @@ function Skeleton() { ) } -function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { +function Snippet({ + thread, + colorMode, +}: { + thread: AppBskyFeedDefs.ThreadViewPost + colorMode: ColorModeValues +}) { const ref = useRef<HTMLInputElement>(null) const [copied, setCopied] = useState(false) @@ -204,9 +241,11 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { // x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x return `<blockquote class="bluesky-embed" data-bluesky-uri="${escapeHtml( thread.post.uri, - )}" data-bluesky-cid="${escapeHtml(thread.post.cid)}"><p lang="${escapeHtml( - lang, - )}">${escapeHtml(record.text)}${ + )}" data-bluesky-cid="${escapeHtml( + thread.post.cid, + )}" data-bluesky-embed-color-mode="${escapeHtml( + colorMode, + )}"><p lang="${escapeHtml(lang)}">${escapeHtml(record.text)}${ record.embed ? `<br><br><a href="${escapeHtml(href)}">[image or embed]</a>` : '' @@ -217,7 +256,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { )}</a>) <a href="${escapeHtml(href)}">${escapeHtml( niceDate(thread.post.indexedAt), )}</a></blockquote><script async src="${EMBED_SCRIPT}" charset="utf-8"></script>` - }, [thread]) + }, [thread, colorMode]) return ( <div className="flex gap-2 w-full"> diff --git a/bskyembed/src/screens/post.tsx b/bskyembed/src/screens/post.tsx index 1764442b7..4cd72b69b 100644 --- a/bskyembed/src/screens/post.tsx +++ b/bskyembed/src/screens/post.tsx @@ -4,7 +4,7 @@ import {AppBskyFeedDefs, AtpAgent} from '@atproto/api' import {h, render} from 'preact' import logo from '../../assets/logo.svg' -import {initColorMode} from '../color-mode' +import {applyTheme, initSystemColorMode} from '../color-mode' import {Container} from '../components/container' import {Link} from '../components/link' import {Post} from '../components/post' @@ -22,7 +22,23 @@ if (!uri) { throw new Error('No uri in path') } -initColorMode() +const query = new URLSearchParams(window.location.search) + +// theme - default to light mode +const colorMode = query.get('colorMode') + +switch (colorMode) { + case 'dark': + applyTheme('dark') + break + case 'system': + initSystemColorMode() + break + case 'light': + default: + applyTheme('light') + break +} agent .getPostThread({ |