v3: various minor visual fixes
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 39s

This commit is contained in:
khannurien
2026-06-28 17:00:44 +00:00
parent fae25f3e6c
commit 324bff3133
12 changed files with 46 additions and 15 deletions

View File

@@ -1022,6 +1022,13 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
a.global-player-title {
color: inherit;
text-decoration: none;
}
a.global-player-title:hover {
text-decoration: underline;
}
.global-player-body { .global-player-body {
display: grid; display: grid;
grid-template-rows: 1fr; grid-template-rows: 1fr;
@@ -4972,8 +4979,10 @@ body.has-fab .page-content {
border-radius: 7px; border-radius: 7px;
background: transparent; background: transparent;
color: var(--color-text); color: var(--color-text);
font-size: 0.9rem; font-size: 0.8rem;
font-weight: 600; font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
white-space: nowrap; white-space: nowrap;
cursor: pointer; cursor: pointer;
transition: background 0.12s; transition: background 0.12s;

View File

@@ -176,11 +176,11 @@ function App() {
<CategoriesProvider> <CategoriesProvider>
<BrowserRouter> <BrowserRouter>
<AppRoutes /> <AppRoutes />
<GlobalPlayer />
</BrowserRouter> </BrowserRouter>
</CategoriesProvider> </CategoriesProvider>
</FollowProvider> </FollowProvider>
</WSProvider> </WSProvider>
<GlobalPlayer />
</PlayerProvider> </PlayerProvider>
</AuthProvider> </AuthProvider>
</ThemeProvider> </ThemeProvider>

View File

@@ -59,6 +59,7 @@ export function DumpCard(
<RichContentCard <RichContentCard
richContent={dump.richContent} richContent={dump.richContent}
compact compact
dumpHref={dumpUrl(dump)}
thumbnailOverrideUrl={dump.thumbnailMime thumbnailOverrideUrl={dump.thumbnailMime
? dumpThumbnailUrl(dump, token) ? dumpThumbnailUrl(dump, token)
: undefined} : undefined}

View File

@@ -1,7 +1,7 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import type { Dump } from "../model.ts"; import type { Dump } from "../model.ts";
import { formatBytes } from "../utils/format.ts"; import { formatBytes } from "../utils/format.ts";
import { dumpFileUrl, dumpThumbnailUrl } from "../utils/urls.ts"; import { dumpFileUrl, dumpThumbnailUrl, dumpUrl } from "../utils/urls.ts";
import { useAuth } from "../hooks/useAuth.ts"; import { useAuth } from "../hooks/useAuth.ts";
import { IconPause, IconPlay, MediaPlayer } from "./MediaPlayer.tsx"; import { IconPause, IconPlay, MediaPlayer } from "./MediaPlayer.tsx";
import { PlayerContext } from "../contexts/PlayerContext.ts"; import { PlayerContext } from "../contexts/PlayerContext.ts";
@@ -45,7 +45,7 @@ function AudioFilePreview(
const handlePlayBtn = () => { const handlePlayBtn = () => {
if (isActive) togglePlay(); if (isActive) togglePlay();
else play({ kind: "file", fileUrl, mimeType: mime, title: dump.title }); else play({ kind: "file", fileUrl, mimeType: mime, title: dump.title, dumpHref: dumpUrl(dump) });
}; };
const handleWaveformClick = (e: React.MouseEvent<Element>) => { const handleWaveformClick = (e: React.MouseEvent<Element>) => {
@@ -59,7 +59,7 @@ function AudioFilePreview(
} else { } else {
// Start playing and seek once it loads — seekTo after play() is a no-op // Start playing and seek once it loads — seekTo after play() is a no-op
// until MediaPlayer mounts; the fraction is best-effort on first click // until MediaPlayer mounts; the fraction is best-effort on first click
play({ kind: "file", fileUrl, mimeType: mime, title: dump.title }); play({ kind: "file", fileUrl, mimeType: mime, title: dump.title, dumpHref: dumpUrl(dump) });
} }
}; };
@@ -178,7 +178,7 @@ export default function FilePreview(
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
play({ kind: "file", fileUrl, mimeType: mime, title: dump.title }); play({ kind: "file", fileUrl, mimeType: mime, title: dump.title, dumpHref: dumpUrl(dump) });
}} }}
> >
<VideoThumb src={thumbUrl} fallback={mimeIcon(mime)} /> <VideoThumb src={thumbUrl} fallback={mimeIcon(mime)} />
@@ -196,7 +196,7 @@ export default function FilePreview(
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
play({ kind: "file", fileUrl, mimeType: mime, title: dump.title }); play({ kind: "file", fileUrl, mimeType: mime, title: dump.title, dumpHref: dumpUrl(dump) });
}} }}
> >
{thumbOverride {thumbOverride

View File

@@ -1,4 +1,5 @@
import { useContext, useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import { Link } from "react-router";
import { PlayerContext } from "../contexts/PlayerContext.ts"; import { PlayerContext } from "../contexts/PlayerContext.ts";
import { MediaPlayer } from "./MediaPlayer.tsx"; import { MediaPlayer } from "./MediaPlayer.tsx";
@@ -75,7 +76,13 @@ export function GlobalPlayer() {
ref={ref} ref={ref}
> >
<div className="global-player-header"> <div className="global-player-header">
<span className="global-player-title">{title}</span> {current.dumpHref
? (
<Link to={current.dumpHref} className="global-player-title">
{title}
</Link>
)
: <span className="global-player-title">{title}</span>}
<button <button
type="button" type="button"
className="btn btn--ghost" className="btn btn--ghost"

View File

@@ -162,6 +162,7 @@ export function JournalCard(
embedUrl, embedUrl,
title: dump.richContent?.title, title: dump.richContent?.title,
type: dump.richContent?.type ?? "unknown", type: dump.richContent?.type ?? "unknown",
dumpHref: dumpUrl(dump),
}) })
: handleNavigate} : handleNavigate}
> >

View File

@@ -22,10 +22,13 @@ interface RichContentCardProps {
compact?: boolean; compact?: boolean;
/** Overrides richContent.thumbnailUrl with a custom dump thumbnail, if set. */ /** Overrides richContent.thumbnailUrl with a custom dump thumbnail, if set. */
thumbnailOverrideUrl?: string; thumbnailOverrideUrl?: string;
/** Link back to the owning dump, surfaced as the global player's title. */
dumpHref?: string;
} }
export default function RichContentCard( export default function RichContentCard(
{ richContent, compact = false, thumbnailOverrideUrl }: RichContentCardProps, { richContent, compact = false, thumbnailOverrideUrl, dumpHref }:
RichContentCardProps,
) { ) {
const { play, current, playing } = useContext(PlayerContext); const { play, current, playing } = useContext(PlayerContext);
const thumbnailSrc = thumbnailOverrideUrl ?? const thumbnailSrc = thumbnailOverrideUrl ??
@@ -52,6 +55,7 @@ export default function RichContentCard(
embedUrl: richContent.embedUrl!, embedUrl: richContent.embedUrl!,
title: richContent.title, title: richContent.title,
type: richContent.type, type: richContent.type,
dumpHref,
}); });
}} }}
aria-label={isPlaying ? "Pause" : "Play"} aria-label={isPlaying ? "Pause" : "Play"}
@@ -113,6 +117,7 @@ export default function RichContentCard(
embedUrl: richContent.embedUrl!, embedUrl: richContent.embedUrl!,
title: richContent.title, title: richContent.title,
type: richContent.type, type: richContent.type,
dumpHref,
})} })}
aria-label="Play" aria-label="Play"
> >

View File

@@ -1,8 +1,8 @@
import { createContext } from "react"; import { createContext } from "react";
export type PlayerItem = export type PlayerItem =
| { kind: "embed"; embedUrl: string; title?: string; type: string } | { kind: "embed"; embedUrl: string; title?: string; type: string; dumpHref?: string }
| { kind: "file"; fileUrl: string; mimeType: string; title?: string }; | { kind: "file"; fileUrl: string; mimeType: string; title?: string; dumpHref?: string };
export interface PlayerContextValue { export interface PlayerContextValue {
// Playback state — readable by any consumer // Playback state — readable by any consumer

View File

@@ -462,6 +462,7 @@ export function Dump() {
? ( ? (
<RichContentCard <RichContentCard
richContent={dump.richContent} richContent={dump.richContent}
dumpHref={dumpUrl(dump)}
thumbnailOverrideUrl={dump.thumbnailMime thumbnailOverrideUrl={dump.thumbnailMime
? dumpThumbnailUrl(dump, token) ? dumpThumbnailUrl(dump, token)
: undefined} : undefined}

View File

@@ -309,7 +309,9 @@
/* ── Category management chips / inline editor ───────────────────── */ /* ── Category management chips / inline editor ───────────────────── */
[data-style="brutalist"] .category-chip, [data-style="brutalist"] .category-chip,
[data-style="brutalist"] .category-editor, [data-style="brutalist"] .category-editor,
[data-style="brutalist"] .category-editor-btn { [data-style="brutalist"] .category-editor-btn,
[data-style="brutalist"] .dump-card-category-chip,
[data-style="brutalist"] .profile-role-chip {
border-radius: 0; border-radius: 0;
} }

View File

@@ -492,7 +492,9 @@
[data-style="geocities"] .feed-tab, [data-style="geocities"] .feed-tab,
[data-style="geocities"] .category-chip, [data-style="geocities"] .category-chip,
[data-style="geocities"] .category-editor, [data-style="geocities"] .category-editor,
[data-style="geocities"] .category-editor-btn { [data-style="geocities"] .category-editor-btn,
[data-style="geocities"] .dump-card-category-chip,
[data-style="geocities"] .profile-role-chip {
border-radius: 0; border-radius: 0;
} }

View File

@@ -245,6 +245,7 @@
[data-style="nyt"] .related-section-title, [data-style="nyt"] .related-section-title,
[data-style="nyt"] .form-page-eyebrow, [data-style="nyt"] .form-page-eyebrow,
[data-style="nyt"] .feed-sort-btn, [data-style="nyt"] .feed-sort-btn,
[data-style="nyt"] .category-switcher-option,
[data-style="nyt"] .follow-btn, [data-style="nyt"] .follow-btn,
[data-style="nyt"] .playlist-badge, [data-style="nyt"] .playlist-badge,
[data-style="nyt"] .dump-card-private-badge, [data-style="nyt"] .dump-card-private-badge,
@@ -529,7 +530,8 @@
[data-style="nyt"] .vote-btn, [data-style="nyt"] .vote-btn,
[data-style="nyt"] .follow-btn, [data-style="nyt"] .follow-btn,
[data-style="nyt"] .btn-add-playlist, [data-style="nyt"] .btn-add-playlist,
[data-style="nyt"] .invite-btn { [data-style="nyt"] .invite-btn,
[data-style="nyt"] .profile-role-chip {
border-radius: 0; border-radius: 0;
border: none; border: none;
} }
@@ -537,7 +539,8 @@
/* Category management chips / inline editor: squared, no accent glow. */ /* Category management chips / inline editor: squared, no accent glow. */
[data-style="nyt"] .category-chip, [data-style="nyt"] .category-chip,
[data-style="nyt"] .category-editor, [data-style="nyt"] .category-editor,
[data-style="nyt"] .category-editor-btn { [data-style="nyt"] .category-editor-btn,
[data-style="nyt"] .dump-card-category-chip {
border-radius: 0; border-radius: 0;
} }