From 2d854091b9684ab253a2c117509bf95609a975a1 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 5 Mar 2025 17:24:59 +0000 Subject: 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> --- .eslintrc.js | 2 +- bskyembed/snippet/embed.ts | 1 + bskyembed/src/color-mode.ts | 8 +++- bskyembed/src/index.css | 11 ++++++ bskyembed/src/screens/landing.tsx | 77 +++++++++++++++++++++++++++++---------- bskyembed/src/screens/post.tsx | 20 +++++++++- 6 files changed, 96 insertions(+), 23 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 5505f45ed..19b869871 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -92,9 +92,9 @@ module.exports = { '*.lock', '.husky', 'patches', - 'bskyweb', '*.html', 'bskyweb', + 'bskyembed', 'src/locale/locales/_build/', 'src/locale/locales/**/*.js', ], 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,"); + 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,"); + } +} 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(, root) function LandingPage() { const [uri, setUri] = useState('') + const [colorMode, setColorMode] = useState('system') const [error, setError] = useState(null) const [loading, setLoading] = useState(false) const [thread, setThread] = useState( @@ -120,24 +125,50 @@ function LandingPage() {

Embed a Bluesky Post

- 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} - /> +
+ setUri(e.currentTarget.value)} + className="border rounded-lg py-3 px-4 dark:bg-dimmedBg dark:border-slate-500" + placeholder={DEFAULT_POST} + /> + +
+ + +
+
{loading ? ( -
+
) : (
- {!error && thread && uri && } - {!error && thread && } + {!error && thread && uri && ( + + )} +
+ {!error && thread && } +
{error && (

{error}

@@ -168,7 +199,13 @@ function Skeleton() { ) } -function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { +function Snippet({ + thread, + colorMode, +}: { + thread: AppBskyFeedDefs.ThreadViewPost + colorMode: ColorModeValues +}) { const ref = useRef(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 `

${escapeHtml(record.text)}${ + )}" data-bluesky-cid="${escapeHtml( + thread.post.cid, + )}" data-bluesky-embed-color-mode="${escapeHtml( + colorMode, + )}">

${escapeHtml(record.text)}${ record.embed ? `

[image or embed]` : '' @@ -217,7 +256,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { )}) ${escapeHtml( niceDate(thread.post.indexedAt), )}

` - }, [thread]) + }, [thread, colorMode]) return (
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({ -- cgit 1.4.1