Change permalink event style

This commit is contained in:
Jon Staab 2026-09-21 10:39:44 -07:00
parent da6a63870a
commit 4b8fcdf196
6 changed files with 23 additions and 25 deletions

View file

@ -21,6 +21,7 @@
import ChatMessageMenu from "@app/components/ChatMessageMenu.svelte" import ChatMessageMenu from "@app/components/ChatMessageMenu.svelte"
import ChatMessageMenuMobile from "@app/components/ChatMessageMenuMobile.svelte" import ChatMessageMenuMobile from "@app/components/ChatMessageMenuMobile.svelte"
import {app, profiles, user} from "@app/core" import {app, profiles, user} from "@app/core"
import {highlightedEvent} from "@app/routes"
import {colorFor} from "@app/theme" import {colorFor} from "@app/theme"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
@ -61,7 +62,10 @@
<ThunkFailure showToastOnRetry {thunk} class="mt-1" /> <ThunkFailure showToastOnRetry {thunk} class="mt-1" />
<div <div
data-event={event.id} data-event={event.id}
class={cx("group flex items-center justify-end gap-1 px-2", {"flex-row-reverse": !isOwn})}> class={cx("group flex items-center justify-end gap-1 px-2", {
"flex-row-reverse": !isOwn,
"highlight-target": $highlightedEvent === event.id,
})}>
{#if !isMobile} {#if !isMobile}
<Tippy <Tippy
bind:controller={tippy} bind:controller={tippy}

View file

@ -47,7 +47,7 @@
import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds" import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds"
import {pageLoading} from "@app/loading" import {pageLoading} from "@app/loading"
import {checked, deferredRoomPath, setChecked} from "@app/notifications" import {checked, deferredRoomPath, setChecked} from "@app/notifications"
import {makeRoomPath} from "@app/routes" import {highlightedEvent, makeRoomPath} from "@app/routes"
import {pendingShare, type Share} from "@app/share" import {pendingShare, type Share} from "@app/share"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
@ -301,7 +301,7 @@
target.scrollIntoView({behavior, block: "center"}) target.scrollIntoView({behavior, block: "center"})
if (highlight) { if (highlight) {
target.classList.add("highlight-target") highlightedEvent.set(id)
} }
if (pin) { if (pin) {

View file

@ -38,7 +38,7 @@
import {ENABLE_ZAPS} from "@app/env" import {ENABLE_ZAPS} from "@app/env"
import type {FeedContext} from "@app/feeds" import type {FeedContext} from "@app/feeds"
import {deriveEvent, deriveEventsForUrl} from "@app/repository" import {deriveEvent, deriveEventsForUrl} from "@app/repository"
import {makeContentPath, makeThreadPath} from "@app/routes" import {highlightedEvent, makeContentPath, makeThreadPath} from "@app/routes"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
type Props = { type Props = {
@ -103,7 +103,7 @@
{onTap} {onTap}
class={cx( class={cx(
"room__item group relative flex w-full cursor-default flex-col px-2 py-0.5 text-left transition-colors", "room__item group relative flex w-full cursor-default flex-col px-2 py-0.5 text-left transition-colors",
{"mt-1.5": showPubkey}, {"mt-1.5": showPubkey, "highlight-target": $highlightedEvent === event.id},
)}> )}>
<div class="flex w-full gap-3 overflow-auto"> <div class="flex w-full gap-3 overflow-auto">
{#if showPubkey} {#if showPubkey}

View file

@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import cx from "classnames"
import {formatTimestamp} from "@welshman/lib" import {formatTimestamp} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
import {COMMENT, displayHandle} from "@welshman/util" import {COMMENT, displayHandle} from "@welshman/util"
@ -14,7 +15,7 @@
import type {FeedContext} from "@app/feeds" import type {FeedContext} from "@app/feeds"
import ThreadActions from "@app/components/ThreadActions.svelte" import ThreadActions from "@app/components/ThreadActions.svelte"
import {handles, profiles} from "@app/core" import {handles, profiles} from "@app/core"
import {makeEventPermalink} from "@app/routes" import {highlightedEvent, makeEventPermalink} from "@app/routes"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {clip} from "@app/toast" import {clip} from "@app/toast"
@ -43,7 +44,7 @@
<article <article
id="post-{event.id}" id="post-{event.id}"
data-event={event.id} data-event={event.id}
class="bg-surface border-b @container" class={cx("bg-surface border-b @container", {"highlight-target": $highlightedEvent === event.id})}
style="border-color: var(--line)"> style="border-color: var(--line)">
<div class="flex flex-col @2xl:flex-row"> <div class="flex flex-col @2xl:flex-row">
<aside <aside

View file

@ -32,6 +32,10 @@ export let lastChatUrl: string | undefined = undefined
export const lastPageBySpaceUrl = new Map<string, string>() export const lastPageBySpaceUrl = new Map<string, string>()
// The event a link pointed at. A store rather than a class set on the node, because each row owns
// its own class attribute and drops anything written behind its back on the next render.
export const highlightedEvent = writable<string | undefined>(undefined)
// The space the user was in most recently, so the space menu can be opened from a page that isn't // The space the user was in most recently, so the space menu can be opened from a page that isn't
// in a space. A store because it's read from markup, unlike lastChatUrl. // in a space. A store because it's read from markup, unlike lastChatUrl.
export const lastSpaceUrl = writable<string | undefined>(undefined) export const lastSpaceUrl = writable<string | undefined>(undefined)
@ -241,7 +245,7 @@ export const scrollToEvent = (id: string) => {
if (element instanceof HTMLElement) { if (element instanceof HTMLElement) {
element.scrollIntoView({behavior: "smooth", block: "center"}) element.scrollIntoView({behavior: "smooth", block: "center"})
element.classList.add("highlight-target") highlightedEvent.set(id)
} }
return element instanceof HTMLElement return element instanceof HTMLElement

View file

@ -129,24 +129,13 @@
width: var(--rule-bleed-r, 100vw); width: var(--rule-bleed-r, 100vw);
} }
} }
/* Marks the event a link pointed at, so the reader can pick it out of the conversation once the /* Marks the event a link pointed at. The mark stays until another link is followed, so the reader
jump lands. Brightness rather than a background so it works over any row's own surface. */ can scroll away and still find their place. The wash is a spread inset shadow rather than a
background because a row's hover rule sets its own background and would paint over it. */
@utility highlight-target { @utility highlight-target {
animation: highlight-target 1.6s ease-out; box-shadow:
} inset 3px 0 0 var(--primary),
@keyframes highlight-target { inset 0 0 0 9999px color-mix(in srgb, var(--primary) 16%, transparent);
0%,
50% {
filter: brightness(1.5);
}
100% {
filter: brightness(1);
}
}
@media (prefers-reduced-motion: reduce) {
.highlight-target {
animation: none;
}
} }
/* Breathes a control in and out while it's listening to the user's voice. */ /* Breathes a control in and out while it's listening to the user's voice. */
@utility pulse-scale { @utility pulse-scale {