feat: fix song view layout - align waveform top, scrollable comments, compose section always visible

This commit is contained in:
Mistral Vibe
2026-04-07 14:36:14 +00:00
parent 5690c9d375
commit ff4985a719

View File

@@ -116,20 +116,22 @@ function Avatar({
} }
return ( return (
<div <div
style={{ style={
width: size, {
height: size, width: size,
borderRadius: "50%", height: size,
background: mc.bg, borderRadius: "50%",
border: `1.5px solid ${mc.border}`, background: mc.bg,
color: mc.text, border: `1.5px solid ${mc.border}`,
display: "flex", color: mc.text,
alignItems: "center", display: "flex",
justifyContent: "center", alignItems: "center",
fontSize: size * 0.38, justifyContent: "center",
fontWeight: 700, fontSize: size * 0.38,
flexShrink: 0, fontWeight: 700,
}} flexShrink: 0,
}
}
> >
{getInitials(name)} {getInitials(name)}
</div> </div>
@@ -200,19 +202,21 @@ function WaveformPins({
return ( return (
<div <div
key={c.id} key={c.id}
style={{ style={
position: "absolute", {
left, position: "absolute",
top: 0, left,
transform: "translateX(-50%)", top: 0,
display: "flex", transform: "translateX(-50%)",
flexDirection: "column", display: "flex",
alignItems: "center", flexDirection: "column",
cursor: "pointer", alignItems: "center",
zIndex: 10, cursor: "pointer",
transition: "transform 0.12s", zIndex: 10,
...(isHovered ? { transform: "translateX(-50%) scale(1.15)" } : {}), transition: "transform 0.12s",
}} ...(isHovered ? { transform: "translateX(-50%) scale(1.15)" } : {}),
}
}
onMouseEnter={() => setHoveredId(c.id)} onMouseEnter={() => setHoveredId(c.id)}
onMouseLeave={() => setHoveredId(null)} onMouseLeave={() => setHoveredId(null)}
onClick={() => { onClick={() => {
@@ -223,34 +227,38 @@ function WaveformPins({
{/* Tooltip */} {/* Tooltip */}
{isHovered && ( {isHovered && (
<div <div
style={{ style={
position: "absolute", {
bottom: "calc(100% + 6px)", position: "absolute",
left: "50%", bottom: "calc(100% + 6px)",
transform: "translateX(-50%)", left: "50%",
background: "#1c1c22", transform: "translateX(-50%)",
border: "1px solid rgba(255,255,255,0.1)", background: "#1c1c22",
borderRadius: 8, border: "1px solid rgba(255,255,255,0.1)",
padding: "8px 10px", borderRadius: 8,
width: 180, padding: "8px 10px",
zIndex: 50, width: 180,
pointerEvents: "none", zIndex: 50,
}} pointerEvents: "none",
}
}
> >
<div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 4 }}> <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 4 }}>
<div <div
style={{ style={
width: 18, {
height: 18, width: 18,
borderRadius: "50%", height: 18,
background: mc.bg, borderRadius: "50%",
color: mc.text, background: mc.bg,
display: "flex", color: mc.text,
alignItems: "center", display: "flex",
justifyContent: "center", alignItems: "center",
fontSize: 8, justifyContent: "center",
fontWeight: 700, fontSize: 8,
}} fontWeight: 700,
}
}
> >
{getInitials(c.author_name)} {getInitials(c.author_name)}
</div> </div>
@@ -266,20 +274,22 @@ function WaveformPins({
)} )}
{/* Avatar circle */} {/* Avatar circle */}
<div <div
style={{ style={
width: 24, {
height: 24, width: 24,
borderRadius: "50%", height: 24,
background: mc.bg, borderRadius: "50%",
border: `2px solid ${mc.border}`, background: mc.bg,
color: mc.text, border: `2px solid ${mc.border}`,
display: "flex", color: mc.text,
alignItems: "center", display: "flex",
justifyContent: "center", alignItems: "center",
fontSize: 9, justifyContent: "center",
fontWeight: 700, fontSize: 9,
boxShadow: "0 2px 8px rgba(0,0,0,0.45)", fontWeight: 700,
}} boxShadow: "0 2px 8px rgba(0,0,0,0.45)",
}
}
> >
{getInitials(c.author_name)} {getInitials(c.author_name)}
</div> </div>
@@ -307,7 +317,7 @@ export function SongPage() {
const [composeFocused, setComposeFocused] = useState(false); const [composeFocused, setComposeFocused] = useState(false);
const [waveformWidth, setWaveformWidth] = useState(0); const [waveformWidth, setWaveformWidth] = useState(0);
// ── Data fetching ──────────────────────────────────────────────────────── // ── Data fetching ──────────────────────────────────────────────────────
const { data: me } = useQuery({ const { data: me } = useQuery({
queryKey: ["me"], queryKey: ["me"],
@@ -338,7 +348,7 @@ export function SongPage() {
enabled: !!songId, enabled: !!songId,
}); });
// ── Version selection ──────────────────────────────────────────────────── // ── Version selection ──────────────────────────────────────────────────
const activeVersion = selectedVersionId ?? versions?.[0]?.id ?? null; const activeVersion = selectedVersionId ?? versions?.[0]?.id ?? null;
@@ -361,7 +371,7 @@ export function SongPage() {
return () => ro.disconnect(); return () => ro.disconnect();
}, []); }, []);
// ── Keyboard shortcut: Space ────────────────────────────────────────────── // ── Keyboard shortcut: Space ────────────────────────────────────────────
useEffect(() => { useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
@@ -401,26 +411,27 @@ export function SongPage() {
}, []); }, []);
// ── Styles ──────────────────────────────────────────────────────────────
// ── Styles ────────────────────────────────────────────────────────────────
const border = "rgba(255,255,255,0.055)"; const border = "rgba(255,255,255,0.055)";
// ── Render ──────────────────────────────────────────────────────────────── // ── Render ──────────────────────────────────────────────────────────────
return ( return (
<div style={{ display: "flex", flexDirection: "column", height: "100%", overflow: "hidden", background: "#0f0f12" }}> <div style={{ display: "flex", flexDirection: "column", height: "100%", overflow: "hidden", background: "#0f0f12" }}>
{/* ── Breadcrumb header ──────────────────────────────────────────── */} {/* ── Breadcrumb header ──────────────────────────────────────────── */}
<div <div
style={{ style={
padding: "11px 20px", {
borderBottom: `1px solid ${border}`, padding: "11px 20px",
display: "flex", borderBottom: `1px solid ${border}`,
alignItems: "center", display: "flex",
gap: 8, alignItems: "center",
flexShrink: 0, gap: 8,
}} flexShrink: 0,
}
}
> >
<div style={{ display: "flex", alignItems: "center", gap: 5, flex: 1, minWidth: 0 }}> <div style={{ display: "flex", alignItems: "center", gap: 5, flex: 1, minWidth: 0 }}>
<button <button
@@ -446,14 +457,16 @@ export function SongPage() {
)} )}
<span style={{ color: "rgba(255,255,255,0.15)", fontSize: 11 }}></span> <span style={{ color: "rgba(255,255,255,0.15)", fontSize: 11 }}></span>
<span <span
style={{ style={
fontSize: 12, {
color: "rgba(255,255,255,0.7)", fontSize: 12,
fontFamily: "'SF Mono', 'Fira Code', monospace", color: "rgba(255,255,255,0.7)",
overflow: "hidden", fontFamily: "'SF Mono', 'Fira Code', monospace",
textOverflow: "ellipsis", overflow: "hidden",
whiteSpace: "nowrap", textOverflow: "ellipsis",
}} whiteSpace: "nowrap",
}
}
> >
{song?.title ?? "…"} {song?.title ?? "…"}
</span> </span>
@@ -466,16 +479,18 @@ export function SongPage() {
<button <button
key={v.id} key={v.id}
onClick={() => setSelectedVersionId(v.id)} onClick={() => setSelectedVersionId(v.id)}
style={{ style={
background: v.id === activeVersion ? "rgba(232,162,42,0.14)" : "transparent", {
border: `1px solid ${v.id === activeVersion ? "rgba(232,162,42,0.28)" : "rgba(255,255,255,0.09)"}`, background: v.id === activeVersion ? "rgba(232,162,42,0.14)" : "transparent",
borderRadius: 6, border: `1px solid ${v.id === activeVersion ? "rgba(232,162,42,0.28)" : "rgba(255,255,255,0.09)"}`,
padding: "4px 10px", borderRadius: 6,
color: v.id === activeVersion ? "#e8a22a" : "rgba(255,255,255,0.38)", padding: "4px 10px",
cursor: "pointer", color: v.id === activeVersion ? "#e8a22a" : "rgba(255,255,255,0.38)",
fontSize: 11, cursor: "pointer",
fontFamily: "monospace", fontSize: 11,
}} fontFamily: "monospace",
}
}
> >
v{v.version_number}{v.label ? ` · ${v.label}` : ""} v{v.version_number}{v.label ? ` · ${v.label}` : ""}
</button> </button>
@@ -484,38 +499,42 @@ export function SongPage() {
)} )}
<button <button
style={{ style={
background: "transparent", {
border: "1px solid rgba(255,255,255,0.09)", background: "transparent",
borderRadius: 6, border: "1px solid rgba(255,255,255,0.09)",
color: "rgba(255,255,255,0.38)", borderRadius: 6,
cursor: "pointer", color: "rgba(255,255,255,0.38)",
fontSize: 12, cursor: "pointer",
padding: "5px 12px", fontSize: 12,
fontFamily: "inherit", padding: "5px 12px",
flexShrink: 0, fontFamily: "inherit",
}} flexShrink: 0,
}
}
> >
Share Share
</button> </button>
</div> </div>
{/* ── Body: waveform | comments ────────────────────────────────── */} {/* ── Body: waveform | comments ────────────────────────────────── */}
<div className="song-page-body" style={{ display: "flex", flex: 1, overflow: "hidden" }}> <div className="song-page-body" style={{ display: "flex", flexDirection: "column", flex: 1, overflow: "hidden" }}>
{/* ── Left: waveform + transport ──────────────────────────────── */} {/* ── Waveform section (top) ──────────────────────────────── */}
<div className="waveform-section" style={{ flex: 1, display: "flex", flexDirection: "column", overflow: "hidden", padding: "16px 20px" }}> <div className="waveform-section" style={{ display: "flex", flexDirection: "column", overflow: "hidden", padding: "16px 20px" }}>
{/* Waveform card */} {/* Waveform card */}
<div <div
style={{ style={
background: "rgba(255,255,255,0.02)", {
border: `1px solid ${border}`, background: "rgba(255,255,255,0.02)",
borderRadius: 10, border: `1px solid ${border}`,
padding: "14px 14px 10px", borderRadius: 10,
marginBottom: 12, padding: "14px 14px 10px",
flexShrink: 0, marginBottom: 12,
}} flexShrink: 0,
}
}
> >
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 6 }}> <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 6 }}>
<span style={{ fontSize: 11, color: "rgba(255,255,255,0.45)", fontFamily: "monospace" }}> <span style={{ fontSize: 11, color: "rgba(255,255,255,0.45)", fontFamily: "monospace" }}>
@@ -559,13 +578,15 @@ export function SongPage() {
{/* Transport */} {/* Transport */}
<div <div
style={{ style={
display: "flex", {
justifyContent: "center", display: "flex",
gap: 10, justifyContent: "center",
padding: "4px 0 12px", gap: 10,
flexShrink: 0, padding: "4px 0 12px",
}} flexShrink: 0,
}
}
> >
{/* Skip back */} {/* Skip back */}
<TransportButton onClick={() => seekTo(Math.max(0, currentTime - 30))} title="30s"> <TransportButton onClick={() => seekTo(Math.max(0, currentTime - 30))} title="30s">
@@ -576,20 +597,22 @@ export function SongPage() {
<button <button
onClick={isPlaying ? pause : play} onClick={isPlaying ? pause : play}
disabled={!activeVersion} disabled={!activeVersion}
style={{ style={
width: 46, {
height: 46, width: 46,
background: "#e8a22a", height: 46,
borderRadius: "50%", background: "#e8a22a",
border: "none", borderRadius: "50%",
display: "flex", border: "none",
alignItems: "center", display: "flex",
justifyContent: "center", alignItems: "center",
cursor: activeVersion ? "pointer" : "default", justifyContent: "center",
opacity: activeVersion ? 1 : 0.4, cursor: activeVersion ? "pointer" : "default",
flexShrink: 0, opacity: activeVersion ? 1 : 0.4,
transition: "background 0.15s, transform 0.15s", flexShrink: 0,
}} transition: "background 0.15s, transform 0.15s",
}
}
onMouseEnter={(e) => { if (activeVersion) e.currentTarget.style.background = "#f0b740"; }} onMouseEnter={(e) => { if (activeVersion) e.currentTarget.style.background = "#f0b740"; }}
onMouseLeave={(e) => { e.currentTarget.style.background = "#e8a22a"; }} onMouseLeave={(e) => { e.currentTarget.style.background = "#e8a22a"; }}
> >
@@ -606,49 +629,207 @@ export function SongPage() {
</div> </div>
{/* ── Right: comment panel ──────────────────────────────────────── */} {/* ── Comments section (bottom) ──────────────────────────────── */}
<div <div
className="comment-panel" className="comment-panel"
style={{ style={
width: 280, {
minWidth: 280, display: "flex",
borderLeft: `1px solid ${border}`, flexDirection: "column",
display: "flex", overflow: "hidden",
flexDirection: "column", background: "rgba(0,0,0,0.12)",
overflow: "hidden", borderTop: `1px solid ${border}`,
background: "rgba(0,0,0,0.12)", flex: 1,
// Responsive: center on mobile }
margin: "0 auto", }
}}
> >
{/* Header */} {/* Header */}
<div <div
style={{ style={
padding: "12px 15px", {
borderBottom: `1px solid ${border}`, padding: "12px 15px",
display: "flex", borderBottom: `1px solid ${border}`,
alignItems: "center", display: "flex",
justifyContent: "space-between", alignItems: "center",
flexShrink: 0, justifyContent: "space-between",
}} flexShrink: 0,
}
}
> >
<span style={{ fontSize: 13, fontWeight: 500, color: "rgba(255,255,255,0.72)" }}>Comments</span> <span style={{ fontSize: 13, fontWeight: 500, color: "rgba(255,255,255,0.72)" }}>Comments</span>
{comments && comments.length > 0 && ( {comments && comments.length > 0 && (
<span <span
style={{ style={
fontSize: 11, {
background: "rgba(232,162,42,0.14)", fontSize: 11,
color: "#e8a22a", background: "rgba(232,162,42,0.14)",
padding: "1px 8px", color: "#e8a22a",
borderRadius: 10, padding: "1px 8px",
}} borderRadius: 10,
}
}
> >
{comments.length} {comments.length}
</span> </span>
)} )}
</div> </div>
{/* Comment list */} {/* Compose section (moved to top) */}
<div
style={
{
padding: "11px 14px",
borderBottom: `1px solid ${border}`,
flexShrink: 0,
}
}
>
<div style={{ display: "flex", gap: 9, alignItems: "flex-start" }}>
{/* My avatar */}
{me ? (
<Avatar name={me.display_name} avatarUrl={me.avatar_url ?? null} authorId={me.id} size={26} />
) : (
<div style={{ width: 26, height: 26, borderRadius: "50%", background: "rgba(255,255,255,0.06)", flexShrink: 0 }} />
)}
<div style={{ flex: 1, minWidth: 0 }}>
{/* Timestamp pill */}
<div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 6 }}>
<div
style={
{
fontSize: 11,
fontFamily: "monospace",
background: "rgba(232,162,42,0.1)",
color: "#e8a22a",
border: "1px solid rgba(232,162,42,0.22)",
padding: "3px 9px",
borderRadius: 20,
display: "flex",
alignItems: "center",
gap: 6,
}
}
>
{isPlaying && (
<div
style={
{
width: 6,
height: 6,
borderRadius: "50%",
background: "#e8a22a",
animation: "rh-blink 1.1s infinite",
}
}
/>
)}
{formatTime(currentTime)}
</div>
<span style={{ fontSize: 11, color: "rgba(255,255,255,0.18)" }}>· pins to playhead</span>
</div>
{/* Textarea */}
<textarea
value={commentBody}
onChange={(e) => setCommentBody(e.target.value)}
onFocus={() => setComposeFocused(true)}
onBlur={() => {
if (!commentBody.trim()) setComposeFocused(false);
}}
placeholder="What do you hear at this moment…"
style={
{
width: "100%",
background: "rgba(255,255,255,0.05)",
border: composeFocused
? "1px solid rgba(232,162,42,0.35)"
: "1px solid rgba(255,255,255,0.07)",
borderRadius: 7,
padding: "8px 10px",
color: "#e0e0e8",
fontSize: 12,
resize: "none",
outline: "none",
fontFamily: "inherit",
height: composeFocused ? 68 : 42,
transition: "height 0.18s, border-color 0.15s",
boxSizing: "border-box",
}
}
/>
{/* Tag buttons + Post (visible when focused) */}
{composeFocused && (
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 7 }}>
<div style={{ display: "flex", gap: 5 }}>
{(["suggestion", "issue", "keeper"] as const).map((tag) => (
<button
key={tag}
onClick={() => setSelectedTag((t) => (t === tag ? "" : tag))}
style={
{
fontSize: 11,
padding: "3px 8px",
borderRadius: 4,
cursor: "pointer",
fontFamily: "inherit",
background:
selectedTag === tag
? TAG_STYLES[tag].bg
: "rgba(255,255,255,0.05)",
border:
selectedTag === tag
? `1px solid ${TAG_STYLES[tag].color}44`
: "1px solid rgba(255,255,255,0.07)",
color:
selectedTag === tag
? TAG_STYLES[tag].color
: "rgba(255,255,255,0.32)",
transition: "all 0.12s",
}
}
>
{tag}
</button>
))}
</div>
<button
onClick={() => {
if (commentBody.trim()) {
addCommentMutation.mutate({
body: commentBody.trim(),
timestamp: currentTime,
tag: selectedTag,
});
}
}}
disabled={!commentBody.trim() || addCommentMutation.isPending}
style={
{
padding: "5px 14px",
borderRadius: 6,
background: "#e8a22a",
border: "none",
color: "#0f0f12",
cursor: commentBody.trim() ? "pointer" : "default",
fontSize: 12,
fontWeight: 600,
fontFamily: "inherit",
opacity: commentBody.trim() ? 1 : 0.35,
transition: "opacity 0.12s",
}
}
>
Post
</button>
</div>
)}
</div>
</div>
</div>
{/* Scrollable comment list */}
<div style={{ flex: 1, overflowY: "auto", padding: "12px 14px" }}> <div style={{ flex: 1, overflowY: "auto", padding: "12px 14px" }}>
{comments?.map((c) => { {comments?.map((c) => {
const tagStyle = c.tag ? TAG_STYLES[c.tag] : null; const tagStyle = c.tag ? TAG_STYLES[c.tag] : null;
@@ -658,15 +839,17 @@ export function SongPage() {
<div <div
key={c.id} key={c.id}
id={`comment-${c.id}`} id={`comment-${c.id}`}
style={{ style={
marginBottom: 14, {
paddingBottom: 14, marginBottom: 14,
borderBottom: "1px solid rgba(255,255,255,0.04)", paddingBottom: 14,
borderRadius: isNearPlayhead ? 6 : undefined, borderBottom: "1px solid rgba(255,255,255,0.04)",
background: isNearPlayhead ? "rgba(232,162,42,0.04)" : undefined, borderRadius: isNearPlayhead ? 6 : undefined,
border: isNearPlayhead ? "1px solid rgba(232,162,42,0.12)" : undefined, background: isNearPlayhead ? "rgba(232,162,42,0.04)" : undefined,
padding: isNearPlayhead ? 8 : undefined, border: isNearPlayhead ? "1px solid rgba(232,162,42,0.12)" : undefined,
}} padding: isNearPlayhead ? 8 : undefined,
}
}
> >
{/* Author row */} {/* Author row */}
<div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 5 }}> <div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 5 }}>
@@ -679,17 +862,19 @@ export function SongPage() {
onClick={() => { onClick={() => {
seekTo(c.timestamp!); seekTo(c.timestamp!);
}} }}
style={{ style={
marginLeft: "auto", {
fontSize: 10, marginLeft: "auto",
fontFamily: "monospace", fontSize: 10,
color: "#e8a22a", fontFamily: "monospace",
background: "rgba(232,162,42,0.1)", color: "#e8a22a",
border: "none", background: "rgba(232,162,42,0.1)",
borderRadius: 3, border: "none",
padding: "1px 5px", borderRadius: 3,
cursor: "pointer", padding: "1px 5px",
}} cursor: "pointer",
}
}
onMouseEnter={(e) => (e.currentTarget.style.background = "rgba(232,162,42,0.2)")} onMouseEnter={(e) => (e.currentTarget.style.background = "rgba(232,162,42,0.2)")}
onMouseLeave={(e) => (e.currentTarget.style.background = "rgba(232,162,42,0.1)")} onMouseLeave={(e) => (e.currentTarget.style.background = "rgba(232,162,42,0.1)")}
> >
@@ -698,13 +883,15 @@ export function SongPage() {
)} )}
{tagStyle && ( {tagStyle && (
<span <span
style={{ style={
fontSize: 10, {
padding: "1px 5px", fontSize: 10,
borderRadius: 3, padding: "1px 5px",
background: tagStyle.bg, borderRadius: 3,
color: tagStyle.color, background: tagStyle.bg,
}} color: tagStyle.color,
}
}
> >
{c.tag} {c.tag}
</span> </span>
@@ -744,148 +931,6 @@ export function SongPage() {
)} )}
</div> </div>
{/* Compose */}
<div
style={{
padding: "11px 14px",
borderTop: `1px solid ${border}`,
flexShrink: 0,
}}
>
<div style={{ display: "flex", gap: 9, alignItems: "flex-start" }}>
{/* My avatar */}
{me ? (
<Avatar name={me.display_name} avatarUrl={me.avatar_url ?? null} authorId={me.id} size={26} />
) : (
<div style={{ width: 26, height: 26, borderRadius: "50%", background: "rgba(255,255,255,0.06)", flexShrink: 0 }} />
)}
<div style={{ flex: 1, minWidth: 0 }}>
{/* Timestamp pill */}
<div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 6 }}>
<div
style={{
fontSize: 11,
fontFamily: "monospace",
background: "rgba(232,162,42,0.1)",
color: "#e8a22a",
border: "1px solid rgba(232,162,42,0.22)",
padding: "3px 9px",
borderRadius: 20,
display: "flex",
alignItems: "center",
gap: 6,
}}
>
{isPlaying && (
<div
style={{
width: 6,
height: 6,
borderRadius: "50%",
background: "#e8a22a",
animation: "rh-blink 1.1s infinite",
}}
/>
)}
{formatTime(currentTime)}
</div>
<span style={{ fontSize: 11, color: "rgba(255,255,255,0.18)" }}>· pins to playhead</span>
</div>
{/* Textarea */}
<textarea
value={commentBody}
onChange={(e) => setCommentBody(e.target.value)}
onFocus={() => setComposeFocused(true)}
onBlur={() => {
if (!commentBody.trim()) setComposeFocused(false);
}}
placeholder="What do you hear at this moment…"
style={{
width: "100%",
background: "rgba(255,255,255,0.05)",
border: composeFocused
? "1px solid rgba(232,162,42,0.35)"
: "1px solid rgba(255,255,255,0.07)",
borderRadius: 7,
padding: "8px 10px",
color: "#e0e0e8",
fontSize: 12,
resize: "none",
outline: "none",
fontFamily: "inherit",
height: composeFocused ? 68 : 42,
transition: "height 0.18s, border-color 0.15s",
boxSizing: "border-box",
}}
/>
{/* Tag buttons + Post (visible when focused) */}
{composeFocused && (
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 7 }}>
<div style={{ display: "flex", gap: 5 }}>
{(["suggestion", "issue", "keeper"] as const).map((tag) => (
<button
key={tag}
onClick={() => setSelectedTag((t) => (t === tag ? "" : tag))}
style={{
fontSize: 11,
padding: "3px 8px",
borderRadius: 4,
cursor: "pointer",
fontFamily: "inherit",
background:
selectedTag === tag
? TAG_STYLES[tag].bg
: "rgba(255,255,255,0.05)",
border:
selectedTag === tag
? `1px solid ${TAG_STYLES[tag].color}44`
: "1px solid rgba(255,255,255,0.07)",
color:
selectedTag === tag
? TAG_STYLES[tag].color
: "rgba(255,255,255,0.32)",
transition: "all 0.12s",
}}
>
{tag}
</button>
))}
</div>
<button
onClick={() => {
if (commentBody.trim()) {
addCommentMutation.mutate({
body: commentBody.trim(),
timestamp: currentTime,
tag: selectedTag,
});
}
}}
disabled={!commentBody.trim() || addCommentMutation.isPending}
style={{
padding: "5px 14px",
borderRadius: 6,
background: "#e8a22a",
border: "none",
color: "#0f0f12",
cursor: commentBody.trim() ? "pointer" : "default",
fontSize: 12,
fontWeight: 600,
fontFamily: "inherit",
opacity: commentBody.trim() ? 1 : 0.35,
transition: "opacity 0.12s",
}}
>
Post
</button>
</div>
)}
</div>
</div>
</div>
</div> </div>
</div> </div>
@@ -928,24 +973,24 @@ function TransportButton({ onClick, title, children }: { onClick: () => void; ti
title={title} title={title}
onMouseEnter={() => setHovered(true)} onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)} onMouseLeave={() => setHovered(false)}
style={{ style={
width: 34, {
height: 34, width: 34,
borderRadius: "50%", height: 34,
background: hovered ? "rgba(255,255,255,0.08)" : "rgba(255,255,255,0.04)", borderRadius: "50%",
border: "1px solid rgba(255,255,255,0.07)", background: hovered ? "rgba(255,255,255,0.08)" : "rgba(255,255,255,0.04)",
display: "flex", border: "1px solid rgba(255,255,255,0.07)",
alignItems: "center", display: "flex",
justifyContent: "center", alignItems: "center",
cursor: "pointer", justifyContent: "center",
color: hovered ? "rgba(255,255,255,0.7)" : "rgba(255,255,255,0.35)", cursor: "pointer",
flexShrink: 0, color: hovered ? "rgba(255,255,255,0.7)" : "rgba(255,255,255,0.35)",
transition: "all 0.12s", flexShrink: 0,
}} transition: "all 0.12s",
}
}
> >
{children} {children}
</button> </button>
); );
} }