development #1

Merged
sschuhmann merged 11 commits from development into main 2026-04-10 07:57:43 +00:00
6 changed files with 81 additions and 81 deletions
Showing only changes of commit 312f3dd161 - Show all commits

View File

@@ -63,7 +63,7 @@ function MiniWave({ songId, active }: { songId: string; active: boolean }) {
height: `${h}%`, height: `${h}%`,
borderRadius: 1, borderRadius: 1,
background: active background: active
? `rgba(139,92,246,${0.3 + (h / 100) * 0.5})` ? `rgba(20,184,166,${0.3 + (h / 100) * 0.5})`
: "rgba(255,255,255,0.1)", : "rgba(255,255,255,0.1)",
transition: "background 0.15s", transition: "background 0.15s",
}} }}
@@ -76,7 +76,7 @@ function MiniWave({ songId, active }: { songId: string; active: boolean }) {
// ── Tag badge ───────────────────────────────────────────────────────────────── // ── Tag badge ─────────────────────────────────────────────────────────────────
const TAG_COLORS: Record<string, { bg: string; color: string }> = { const TAG_COLORS: Record<string, { bg: string; color: string }> = {
jam: { bg: "rgba(139,92,246,0.12)", color: "#a78bfa" }, jam: { bg: "rgba(20,184,166,0.12)", color: "#2dd4bf" },
riff: { bg: "rgba(34,211,238,0.1)", color: "#67e8f9" }, riff: { bg: "rgba(34,211,238,0.1)", color: "#67e8f9" },
idea: { bg: "rgba(52,211,153,0.1)", color: "#6ee7b7" }, idea: { bg: "rgba(52,211,153,0.1)", color: "#6ee7b7" },
}; };
@@ -117,19 +117,19 @@ function TrackRow({
padding: "9px 20px", padding: "9px 20px",
cursor: "pointer", cursor: "pointer",
position: "relative", position: "relative",
background: active ? "rgba(139,92,246,0.06)" : hovered ? "rgba(255,255,255,0.025)" : "transparent", background: active ? "rgba(20,184,166,0.06)" : hovered ? "rgba(255,255,255,0.025)" : "transparent",
transition: "background 0.12s", transition: "background 0.12s",
}} }}
> >
{active && ( {active && (
<div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: 2, background: "linear-gradient(to bottom, #7b5cf6, #22d3ee)" }} /> <div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: 2, background: "linear-gradient(to bottom, #0d9488, #22d3ee)" }} />
)} )}
<span style={{ fontSize: 10, color: "rgba(232,233,240,0.2)", width: 20, textAlign: "right", flexShrink: 0, fontFamily: "monospace" }}> <span style={{ fontSize: 10, color: "rgba(232,233,240,0.2)", width: 20, textAlign: "right", flexShrink: 0, fontFamily: "monospace" }}>
{String(index + 1).padStart(2, "0")} {String(index + 1).padStart(2, "0")}
</span> </span>
<span style={{ fontSize: 13, fontWeight: 600, flex: 1, color: active ? "#a78bfa" : "#e8e9f0", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", transition: "color 0.12s" }}> <span style={{ fontSize: 13, fontWeight: 600, flex: 1, color: active ? "#2dd4bf" : "#e8e9f0", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", transition: "color 0.12s" }}>
{song.title} {song.title}
</span> </span>
@@ -191,7 +191,7 @@ function SessionGroup({
> >
<span style={{ <span style={{
fontSize: 10, fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", fontSize: 10, fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase",
background: "linear-gradient(135deg, #7b5cf6, #22d3ee)", background: "linear-gradient(135deg, #0d9488, #22d3ee)",
WebkitBackgroundClip: "text", WebkitTextFillColor: "transparent", WebkitBackgroundClip: "text", WebkitTextFillColor: "transparent",
}}> }}>
{formatSessionDate(session.date)} {formatSessionDate(session.date)}
@@ -280,7 +280,7 @@ export function LibraryPanel({ bandId, selectedSongId, onSelectSong }: LibraryPa
flex: 1, flex: 1,
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
background: "#10131f", background: "#0c1612",
height: "100%", height: "100%",
overflow: "hidden", overflow: "hidden",
}}> }}>
@@ -292,8 +292,8 @@ export function LibraryPanel({ bandId, selectedSongId, onSelectSong }: LibraryPa
</h2> </h2>
{/* Search */} {/* Search */}
<div style={{ display: "flex", alignItems: "center", gap: 8, background: "#151828", border: `1px solid ${border}`, borderRadius: 8, padding: "8px 12px", transition: "border-color 0.15s" }} <div style={{ display: "flex", alignItems: "center", gap: 8, background: "#101c18", border: `1px solid ${border}`, borderRadius: 8, padding: "8px 12px", transition: "border-color 0.15s" }}
onFocusCapture={(e) => (e.currentTarget.style.borderColor = "rgba(139,92,246,0.4)")} onFocusCapture={(e) => (e.currentTarget.style.borderColor = "rgba(20,184,166,0.4)")}
onBlurCapture={(e) => (e.currentTarget.style.borderColor = border)} onBlurCapture={(e) => (e.currentTarget.style.borderColor = border)}
> >
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ color: "rgba(232,233,240,0.25)", flexShrink: 0 }}> <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ color: "rgba(232,233,240,0.25)", flexShrink: 0 }}>
@@ -304,7 +304,7 @@ export function LibraryPanel({ bandId, selectedSongId, onSelectSong }: LibraryPa
value={search} value={search}
onChange={(e) => setSearch(e.target.value)} onChange={(e) => setSearch(e.target.value)}
placeholder="Search recordings…" placeholder="Search recordings…"
style={{ background: "none", border: "none", outline: "none", fontFamily: "inherit", fontSize: 13, color: "#e8e9f0", flex: 1, caretColor: "#a78bfa" }} style={{ background: "none", border: "none", outline: "none", fontFamily: "inherit", fontSize: 13, color: "#e8e9f0", flex: 1, caretColor: "#2dd4bf" }}
/> />
{search && ( {search && (
<button onClick={() => setSearch("")} style={{ background: "none", border: "none", cursor: "pointer", color: "rgba(232,233,240,0.3)", padding: 0, display: "flex", lineHeight: 1 }}> <button onClick={() => setSearch("")} style={{ background: "none", border: "none", cursor: "pointer", color: "rgba(232,233,240,0.3)", padding: 0, display: "flex", lineHeight: 1 }}>
@@ -324,9 +324,9 @@ export function LibraryPanel({ bandId, selectedSongId, onSelectSong }: LibraryPa
onClick={() => setFilterTag(chip.value)} onClick={() => setFilterTag(chip.value)}
style={{ style={{
fontSize: 11, fontWeight: 600, padding: "4px 12px", borderRadius: 20, cursor: "pointer", whiteSpace: "nowrap", fontSize: 11, fontWeight: 600, padding: "4px 12px", borderRadius: 20, cursor: "pointer", whiteSpace: "nowrap",
border: on ? "1px solid rgba(139,92,246,0.4)" : `1px solid ${border}`, border: on ? "1px solid rgba(20,184,166,0.4)" : `1px solid ${border}`,
background: on ? "rgba(139,92,246,0.1)" : "transparent", background: on ? "rgba(20,184,166,0.1)" : "transparent",
color: on ? "#a78bfa" : "rgba(232,233,240,0.35)", color: on ? "#2dd4bf" : "rgba(232,233,240,0.35)",
fontFamily: "inherit", fontFamily: "inherit",
transition: "all 0.12s", transition: "all 0.12s",
}} }}

View File

@@ -62,7 +62,7 @@ const MEMBER_COLORS = [
{ bg: "rgba(91,156,240,0.18)", border: "rgba(91,156,240,0.6)", text: "#7aabf0" }, { bg: "rgba(91,156,240,0.18)", border: "rgba(91,156,240,0.6)", text: "#7aabf0" },
{ bg: "rgba(200,90,180,0.18)", border: "rgba(200,90,180,0.6)", text: "#d070c0" }, { bg: "rgba(200,90,180,0.18)", border: "rgba(200,90,180,0.6)", text: "#d070c0" },
{ bg: "rgba(52,211,153,0.18)", border: "rgba(52,211,153,0.6)", text: "#34d399" }, { bg: "rgba(52,211,153,0.18)", border: "rgba(52,211,153,0.6)", text: "#34d399" },
{ bg: "rgba(139,92,246,0.18)", border: "rgba(139,92,246,0.6)", text: "#a78bfa" }, { bg: "rgba(20,184,166,0.18)", border: "rgba(20,184,166,0.6)", text: "#2dd4bf" },
{ bg: "rgba(34,211,238,0.18)", border: "rgba(34,211,238,0.6)", text: "#22d3ee" }, { bg: "rgba(34,211,238,0.18)", border: "rgba(34,211,238,0.6)", text: "#22d3ee" },
]; ];
@@ -124,13 +124,13 @@ function WaveformPins({
onClick={() => { onSeek(c.timestamp!); onScrollToComment(c.id); }} onClick={() => { onSeek(c.timestamp!); onScrollToComment(c.id); }}
> >
{isHovered && ( {isHovered && (
<div style={{ position: "absolute", bottom: "calc(100% + 6px)", left: "50%", transform: "translateX(-50%)", background: "#1a1e30", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 8, padding: "8px 10px", width: 180, zIndex: 50, pointerEvents: "none" }}> <div style={{ position: "absolute", bottom: "calc(100% + 6px)", left: "50%", transform: "translateX(-50%)", background: "#142420", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 8, padding: "8px 10px", width: 180, 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 style={{ width: 18, height: 18, borderRadius: "50%", background: mc.bg, color: mc.text, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 8, fontWeight: 700 }}> <div style={{ width: 18, height: 18, borderRadius: "50%", background: mc.bg, color: mc.text, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 8, fontWeight: 700 }}>
{getInitials(c.author_name)} {getInitials(c.author_name)}
</div> </div>
<span style={{ fontSize: 11, fontWeight: 500, color: "rgba(232,233,240,0.72)" }}>{c.author_name}</span> <span style={{ fontSize: 11, fontWeight: 500, color: "rgba(232,233,240,0.72)" }}>{c.author_name}</span>
<span style={{ fontSize: 10, fontFamily: "monospace", color: "#a78bfa", marginLeft: "auto" }}>{formatTime(c.timestamp!)}</span> <span style={{ fontSize: 10, fontFamily: "monospace", color: "#2dd4bf", marginLeft: "auto" }}>{formatTime(c.timestamp!)}</span>
</div> </div>
<div style={{ fontSize: 11, color: "rgba(232,233,240,0.42)", lineHeight: 1.4 }}> <div style={{ fontSize: 11, color: "rgba(232,233,240,0.42)", lineHeight: 1.4 }}>
{c.body.length > 80 ? c.body.slice(0, 80) + "…" : c.body} {c.body.length > 80 ? c.body.slice(0, 80) + "…" : c.body}
@@ -271,7 +271,7 @@ export function PlayerPanel({ songId, bandId, onBack }: PlayerPanelProps) {
// ── Render ──────────────────────────────────────────────────────────────── // ── Render ────────────────────────────────────────────────────────────────
return ( return (
<div style={{ display: "flex", flexDirection: "column", flex: 1, height: "100%", overflow: "hidden", background: "#0c0e1a", minWidth: 0 }}> <div style={{ display: "flex", flexDirection: "column", flex: 1, height: "100%", overflow: "hidden", background: "#080f0d", minWidth: 0 }}>
{/* Breadcrumb / header */} {/* Breadcrumb / header */}
<div style={{ padding: "11px 20px", borderBottom: `1px solid ${border}`, display: "flex", alignItems: "center", gap: 8, flexShrink: 0 }}> <div style={{ padding: "11px 20px", borderBottom: `1px solid ${border}`, display: "flex", alignItems: "center", gap: 8, flexShrink: 0 }}>
@@ -304,7 +304,7 @@ export function PlayerPanel({ songId, bandId, onBack }: PlayerPanelProps) {
<div style={{ display: "flex", gap: 4, flexShrink: 0 }}> <div style={{ display: "flex", gap: 4, flexShrink: 0 }}>
{versions.map((v) => ( {versions.map((v) => (
<button key={v.id} onClick={() => setSelectedVersionId(v.id)} <button key={v.id} onClick={() => setSelectedVersionId(v.id)}
style={{ background: v.id === activeVersion ? "rgba(139,92,246,0.12)" : "transparent", border: `1px solid ${v.id === activeVersion ? "rgba(139,92,246,0.3)" : "rgba(255,255,255,0.09)"}`, borderRadius: 6, padding: "4px 10px", color: v.id === activeVersion ? "#a78bfa" : "rgba(232,233,240,0.38)", cursor: "pointer", fontSize: 11, fontFamily: "monospace" }}> style={{ background: v.id === activeVersion ? "rgba(20,184,166,0.12)" : "transparent", border: `1px solid ${v.id === activeVersion ? "rgba(20,184,166,0.3)" : "rgba(255,255,255,0.09)"}`, borderRadius: 6, padding: "4px 10px", color: v.id === activeVersion ? "#2dd4bf" : "rgba(232,233,240,0.38)", cursor: "pointer", fontSize: 11, fontFamily: "monospace" }}>
v{v.version_number}{v.label ? ` · ${v.label}` : ""} v{v.version_number}{v.label ? ` · ${v.label}` : ""}
</button> </button>
))} ))}
@@ -340,7 +340,7 @@ export function PlayerPanel({ songId, bandId, onBack }: PlayerPanelProps) {
{/* Time bar */} {/* Time bar */}
<div style={{ display: "flex", justifyContent: "space-between", marginTop: 6, padding: "0 1px" }}> <div style={{ display: "flex", justifyContent: "space-between", marginTop: 6, padding: "0 1px" }}>
<span style={{ fontSize: 10, fontFamily: "monospace", color: "#a78bfa" }}>{formatTime(currentTime)}</span> <span style={{ fontSize: 10, fontFamily: "monospace", color: "#2dd4bf" }}>{formatTime(currentTime)}</span>
<span style={{ fontSize: 10, fontFamily: "monospace", color: "rgba(232,233,240,0.22)" }}>{isReady && duration > 0 ? formatTime(duration / 2) : "—"}</span> <span style={{ fontSize: 10, fontFamily: "monospace", color: "rgba(232,233,240,0.22)" }}>{isReady && duration > 0 ? formatTime(duration / 2) : "—"}</span>
<span style={{ fontSize: 10, fontFamily: "monospace", color: "rgba(232,233,240,0.22)" }}>{isReady ? formatTime(duration) : "—"}</span> <span style={{ fontSize: 10, fontFamily: "monospace", color: "rgba(232,233,240,0.22)" }}>{isReady ? formatTime(duration) : "—"}</span>
</div> </div>
@@ -352,9 +352,9 @@ export function PlayerPanel({ songId, bandId, onBack }: PlayerPanelProps) {
<button <button
onClick={isPlaying ? pause : play} onClick={isPlaying ? pause : play}
disabled={!activeVersion} disabled={!activeVersion}
style={{ width: 46, height: 46, background: "linear-gradient(135deg, #7b5cf6, #06b6d4)", borderRadius: "50%", border: "none", display: "flex", alignItems: "center", justifyContent: "center", cursor: activeVersion ? "pointer" : "default", opacity: activeVersion ? 1 : 0.4, flexShrink: 0, transition: "transform 0.12s, box-shadow 0.12s", boxShadow: "0 4px 16px rgba(139,92,246,0.35)" }} style={{ width: 46, height: 46, background: "linear-gradient(135deg, #0d9488, #06b6d4)", borderRadius: "50%", border: "none", display: "flex", alignItems: "center", justifyContent: "center", cursor: activeVersion ? "pointer" : "default", opacity: activeVersion ? 1 : 0.4, flexShrink: 0, transition: "transform 0.12s, box-shadow 0.12s", boxShadow: "0 4px 16px rgba(20,184,166,0.35)" }}
onMouseEnter={(e) => { if (activeVersion) { e.currentTarget.style.boxShadow = "0 6px 24px rgba(139,92,246,0.55)"; e.currentTarget.style.transform = "scale(1.04)"; } }} onMouseEnter={(e) => { if (activeVersion) { e.currentTarget.style.boxShadow = "0 6px 24px rgba(20,184,166,0.55)"; e.currentTarget.style.transform = "scale(1.04)"; } }}
onMouseLeave={(e) => { e.currentTarget.style.boxShadow = "0 4px 16px rgba(139,92,246,0.35)"; e.currentTarget.style.transform = "scale(1)"; }} onMouseLeave={(e) => { e.currentTarget.style.boxShadow = "0 4px 16px rgba(20,184,166,0.35)"; e.currentTarget.style.transform = "scale(1)"; }}
> >
{isPlaying ? <IconPause /> : <IconPlay />} {isPlaying ? <IconPause /> : <IconPlay />}
</button> </button>
@@ -369,7 +369,7 @@ export function PlayerPanel({ songId, bandId, onBack }: PlayerPanelProps) {
<div style={{ padding: "12px 15px", borderBottom: `1px solid ${border}`, display: "flex", alignItems: "center", justifyContent: "space-between", flexShrink: 0 }}> <div style={{ padding: "12px 15px", borderBottom: `1px solid ${border}`, display: "flex", alignItems: "center", justifyContent: "space-between", flexShrink: 0 }}>
<span style={{ fontSize: 13, fontWeight: 500, color: "rgba(232,233,240,0.72)" }}>Comments</span> <span style={{ fontSize: 13, fontWeight: 500, color: "rgba(232,233,240,0.72)" }}>Comments</span>
{comments && comments.length > 0 && ( {comments && comments.length > 0 && (
<span style={{ fontSize: 11, background: "rgba(139,92,246,0.12)", color: "#a78bfa", padding: "1px 8px", borderRadius: 10 }}>{comments.length}</span> <span style={{ fontSize: 11, background: "rgba(20,184,166,0.12)", color: "#2dd4bf", padding: "1px 8px", borderRadius: 10 }}>{comments.length}</span>
)} )}
</div> </div>
@@ -383,8 +383,8 @@ export function PlayerPanel({ songId, bandId, onBack }: PlayerPanelProps) {
)} )}
<div style={{ flex: 1, minWidth: 0 }}> <div style={{ flex: 1, minWidth: 0 }}>
<div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 6 }}> <div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 6 }}>
<div style={{ fontSize: 11, fontFamily: "monospace", background: "rgba(139,92,246,0.1)", color: "#a78bfa", border: "1px solid rgba(139,92,246,0.22)", padding: "3px 9px", borderRadius: 20, display: "flex", alignItems: "center", gap: 6 }}> <div style={{ fontSize: 11, fontFamily: "monospace", background: "rgba(20,184,166,0.1)", color: "#2dd4bf", border: "1px solid rgba(20,184,166,0.22)", padding: "3px 9px", borderRadius: 20, display: "flex", alignItems: "center", gap: 6 }}>
{isPlaying && <div style={{ width: 6, height: 6, borderRadius: "50%", background: "#a78bfa", animation: "pp-blink 1.1s infinite" }} />} {isPlaying && <div style={{ width: 6, height: 6, borderRadius: "50%", background: "#2dd4bf", animation: "pp-blink 1.1s infinite" }} />}
{formatTime(currentTime)} {formatTime(currentTime)}
</div> </div>
<span style={{ fontSize: 11, color: "rgba(232,233,240,0.2)" }}>· pins to playhead</span> <span style={{ fontSize: 11, color: "rgba(232,233,240,0.2)" }}>· pins to playhead</span>
@@ -395,7 +395,7 @@ export function PlayerPanel({ songId, bandId, onBack }: PlayerPanelProps) {
onFocus={() => setComposeFocused(true)} onFocus={() => setComposeFocused(true)}
onBlur={() => { if (!commentBody.trim()) setComposeFocused(false); }} onBlur={() => { if (!commentBody.trim()) setComposeFocused(false); }}
placeholder="What do you hear at this moment…" placeholder="What do you hear at this moment…"
style={{ width: "100%", background: "rgba(255,255,255,0.04)", border: composeFocused ? "1px solid rgba(139,92,246,0.35)" : `1px solid rgba(255,255,255,0.07)`, borderRadius: 7, padding: "8px 10px", color: "#e8e9f0", fontSize: 12, resize: "none", outline: "none", fontFamily: "inherit", height: composeFocused ? 68 : 42, transition: "height 0.18s, border-color 0.15s", boxSizing: "border-box" }} style={{ width: "100%", background: "rgba(255,255,255,0.04)", border: composeFocused ? "1px solid rgba(20,184,166,0.35)" : `1px solid rgba(255,255,255,0.07)`, borderRadius: 7, padding: "8px 10px", color: "#e8e9f0", fontSize: 12, resize: "none", outline: "none", fontFamily: "inherit", height: composeFocused ? 68 : 42, transition: "height 0.18s, border-color 0.15s", boxSizing: "border-box" }}
/> />
{composeFocused && ( {composeFocused && (
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 7 }}> <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 7 }}>
@@ -410,7 +410,7 @@ export function PlayerPanel({ songId, bandId, onBack }: PlayerPanelProps) {
<button <button
onClick={() => { if (commentBody.trim()) addCommentMutation.mutate({ body: commentBody.trim(), timestamp: currentTime, tag: selectedTag }); }} onClick={() => { if (commentBody.trim()) addCommentMutation.mutate({ body: commentBody.trim(), timestamp: currentTime, tag: selectedTag }); }}
disabled={!commentBody.trim() || addCommentMutation.isPending} disabled={!commentBody.trim() || addCommentMutation.isPending}
style={{ padding: "5px 14px", borderRadius: 6, background: "linear-gradient(135deg, #7b5cf6, #06b6d4)", border: "none", color: "white", cursor: commentBody.trim() ? "pointer" : "default", fontSize: 12, fontWeight: 600, fontFamily: "inherit", opacity: commentBody.trim() ? 1 : 0.35, transition: "opacity 0.12s" }}> style={{ padding: "5px 14px", borderRadius: 6, background: "linear-gradient(135deg, #0d9488, #06b6d4)", border: "none", color: "white", cursor: commentBody.trim() ? "pointer" : "default", fontSize: 12, fontWeight: 600, fontFamily: "inherit", opacity: commentBody.trim() ? 1 : 0.35, transition: "opacity 0.12s" }}>
Post Post
</button> </button>
</div> </div>
@@ -427,15 +427,15 @@ export function PlayerPanel({ songId, bandId, onBack }: PlayerPanelProps) {
return ( return (
<div key={c.id} id={`comment-${c.id}`} <div key={c.id} id={`comment-${c.id}`}
style={{ marginBottom: 14, paddingBottom: 14, borderBottom: `1px solid rgba(255,255,255,0.04)`, borderRadius: isNearPlayhead ? 6 : undefined, background: isNearPlayhead ? "rgba(139,92,246,0.04)" : undefined, border: isNearPlayhead ? "1px solid rgba(139,92,246,0.12)" : undefined, padding: isNearPlayhead ? 8 : undefined }}> style={{ marginBottom: 14, paddingBottom: 14, borderBottom: `1px solid rgba(255,255,255,0.04)`, borderRadius: isNearPlayhead ? 6 : undefined, background: isNearPlayhead ? "rgba(20,184,166,0.04)" : undefined, border: isNearPlayhead ? "1px solid rgba(20,184,166,0.12)" : undefined, padding: isNearPlayhead ? 8 : undefined }}>
<div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 5 }}> <div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 5 }}>
<Avatar name={c.author_name} avatarUrl={c.author_avatar_url} authorId={c.author_id} size={21} /> <Avatar name={c.author_name} avatarUrl={c.author_avatar_url} authorId={c.author_id} size={21} />
<span style={{ fontSize: 12, fontWeight: 500, color: "rgba(232,233,240,0.72)" }}>{c.author_name}</span> <span style={{ fontSize: 12, fontWeight: 500, color: "rgba(232,233,240,0.72)" }}>{c.author_name}</span>
{c.timestamp != null && ( {c.timestamp != null && (
<button onClick={() => seekTo(c.timestamp!)} <button onClick={() => seekTo(c.timestamp!)}
style={{ marginLeft: "auto", fontSize: 10, fontFamily: "monospace", color: "#a78bfa", background: "rgba(139,92,246,0.1)", border: "none", borderRadius: 3, padding: "1px 5px", cursor: "pointer" }} style={{ marginLeft: "auto", fontSize: 10, fontFamily: "monospace", color: "#2dd4bf", background: "rgba(20,184,166,0.1)", border: "none", borderRadius: 3, padding: "1px 5px", cursor: "pointer" }}
onMouseEnter={(e) => (e.currentTarget.style.background = "rgba(139,92,246,0.2)")} onMouseEnter={(e) => (e.currentTarget.style.background = "rgba(20,184,166,0.2)")}
onMouseLeave={(e) => (e.currentTarget.style.background = "rgba(139,92,246,0.1)")} onMouseLeave={(e) => (e.currentTarget.style.background = "rgba(20,184,166,0.1)")}
> >
{formatTime(c.timestamp)} {formatTime(c.timestamp)}
</button> </button>

View File

@@ -71,7 +71,7 @@ function NavItem({ icon, label, active, onClick, disabled, badge, collapsed }: N
const [hovered, setHovered] = useState(false); const [hovered, setHovered] = useState(false);
const fg = active const fg = active
? "#a78bfa" ? "#2dd4bf"
: disabled : disabled
? "rgba(255,255,255,0.16)" ? "rgba(255,255,255,0.16)"
: hovered : hovered
@@ -79,7 +79,7 @@ function NavItem({ icon, label, active, onClick, disabled, badge, collapsed }: N
: "rgba(232,233,240,0.35)"; : "rgba(232,233,240,0.35)";
const bg = active const bg = active
? "rgba(139,92,246,0.12)" ? "rgba(20,184,166,0.12)"
: hovered && !disabled : hovered && !disabled
? "rgba(255,255,255,0.04)" ? "rgba(255,255,255,0.04)"
: "transparent"; : "transparent";
@@ -115,7 +115,7 @@ function NavItem({ icon, label, active, onClick, disabled, badge, collapsed }: N
<div style={{ <div style={{
position: "absolute", left: 0, top: "20%", bottom: "20%", position: "absolute", left: 0, top: "20%", bottom: "20%",
width: 2, borderRadius: "0 2px 2px 0", width: 2, borderRadius: "0 2px 2px 0",
background: "linear-gradient(to bottom, #7b5cf6, #22d3ee)", background: "linear-gradient(to bottom, #0d9488, #22d3ee)",
}} /> }} />
)} )}
<span style={{ width: 20, height: 20, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}> <span style={{ width: 20, height: 20, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
@@ -129,7 +129,7 @@ function NavItem({ icon, label, active, onClick, disabled, badge, collapsed }: N
{!collapsed && badge != null && badge > 0 && ( {!collapsed && badge != null && badge > 0 && (
<span style={{ <span style={{
fontSize: 9, fontWeight: 700, padding: "2px 6px", borderRadius: 20, fontSize: 9, fontWeight: 700, padding: "2px 6px", borderRadius: 20,
background: "linear-gradient(135deg, #7b5cf6, #22d3ee)", color: "white", background: "linear-gradient(135deg, #0d9488, #22d3ee)", color: "white",
flexShrink: 0, flexShrink: 0,
}}> }}>
{badge} {badge}
@@ -168,13 +168,13 @@ export function Sidebar({ children }: { children: React.ReactNode }) {
const border = "rgba(255,255,255,0.06)"; const border = "rgba(255,255,255,0.06)";
return ( return (
<div style={{ display: "flex", height: "100vh", overflow: "hidden", background: "#0c0e1a", color: "#e8e9f0", fontFamily: "-apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif", fontSize: 13 }}> <div style={{ display: "flex", height: "100vh", overflow: "hidden", background: "#080f0d", color: "#e8e9f0", fontFamily: "-apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif", fontSize: 13 }}>
{/* ── Sidebar ── */} {/* ── Sidebar ── */}
<aside style={{ <aside style={{
width: sidebarWidth, width: sidebarWidth,
minWidth: sidebarWidth, minWidth: sidebarWidth,
background: "#10131f", background: "#0c1612",
borderRight: `1px solid ${border}`, borderRight: `1px solid ${border}`,
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
@@ -191,14 +191,14 @@ export function Sidebar({ children }: { children: React.ReactNode }) {
title={collapsed ? "Expand menu" : "Collapse menu"} title={collapsed ? "Expand menu" : "Collapse menu"}
style={{ style={{
width: 40, height: 40, borderRadius: 12, flexShrink: 0, width: 40, height: 40, borderRadius: 12, flexShrink: 0,
background: "linear-gradient(135deg, #7b5cf6, #06b6d4)", background: "linear-gradient(135deg, #0d9488, #06b6d4)",
display: "flex", alignItems: "center", justifyContent: "center", display: "flex", alignItems: "center", justifyContent: "center",
border: "none", cursor: "pointer", border: "none", cursor: "pointer",
boxShadow: "0 0 20px rgba(139,92,246,0.3)", boxShadow: "0 0 20px rgba(20,184,166,0.3)",
transition: "box-shadow 0.2s", transition: "box-shadow 0.2s",
}} }}
onMouseEnter={(e) => (e.currentTarget.style.boxShadow = "0 0 30px rgba(139,92,246,0.5)")} onMouseEnter={(e) => (e.currentTarget.style.boxShadow = "0 0 30px rgba(20,184,166,0.5)")}
onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "0 0 20px rgba(139,92,246,0.3)")} onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "0 0 20px rgba(20,184,166,0.3)")}
> >
<IconMenu /> <IconMenu />
</button> </button>
@@ -269,7 +269,7 @@ export function Sidebar({ children }: { children: React.ReactNode }) {
</aside> </aside>
{/* ── Main content ── */} {/* ── Main content ── */}
<main style={{ flex: 1, overflow: "hidden", display: "grid", gridTemplateRows: "44px 1fr", background: "#0c0e1a", minWidth: 0 }}> <main style={{ flex: 1, overflow: "hidden", display: "grid", gridTemplateRows: "44px 1fr", background: "#080f0d", minWidth: 0 }}>
<TopBandBar /> <TopBandBar />
<div style={{ overflow: "auto", display: "flex", flexDirection: "column" }}> <div style={{ overflow: "auto", display: "flex", flexDirection: "column" }}>
{children} {children}

View File

@@ -48,7 +48,7 @@ export function TopBandBar() {
gap: 8, gap: 8,
padding: "0 20px", padding: "0 20px",
borderBottom: `1px solid ${border}`, borderBottom: `1px solid ${border}`,
background: "#10131f", background: "#0c1612",
zIndex: 10, zIndex: 10,
}}> }}>
{/* Band switcher */} {/* Band switcher */}
@@ -72,10 +72,10 @@ export function TopBandBar() {
<> <>
<div style={{ <div style={{
width: 22, height: 22, borderRadius: 6, flexShrink: 0, width: 22, height: 22, borderRadius: 6, flexShrink: 0,
background: "rgba(139,92,246,0.15)", background: "rgba(20,184,166,0.15)",
border: "1px solid rgba(139,92,246,0.3)", border: "1px solid rgba(20,184,166,0.3)",
display: "flex", alignItems: "center", justifyContent: "center", display: "flex", alignItems: "center", justifyContent: "center",
fontSize: 9, fontWeight: 800, color: "#a78bfa", fontSize: 9, fontWeight: 800, color: "#2dd4bf",
}}> }}>
{getInitials(activeBand.name)} {getInitials(activeBand.name)}
</div> </div>
@@ -93,7 +93,7 @@ export function TopBandBar() {
<div style={{ <div style={{
position: "absolute", top: "calc(100% + 6px)", left: 0, position: "absolute", top: "calc(100% + 6px)", left: 0,
minWidth: 220, minWidth: 220,
background: "#1a1e30", background: "#142420",
border: "1px solid rgba(255,255,255,0.1)", border: "1px solid rgba(255,255,255,0.1)",
borderRadius: 10, padding: 6, zIndex: 100, borderRadius: 10, padding: 6, zIndex: 100,
boxShadow: "0 8px 32px rgba(0,0,0,0.5)", boxShadow: "0 8px 32px rgba(0,0,0,0.5)",
@@ -109,7 +109,7 @@ export function TopBandBar() {
style={{ style={{
width: "100%", display: "flex", alignItems: "center", gap: 8, width: "100%", display: "flex", alignItems: "center", gap: 8,
padding: "7px 9px", marginBottom: 1, padding: "7px 9px", marginBottom: 1,
background: band.id === currentBandId ? "rgba(139,92,246,0.1)" : "transparent", background: band.id === currentBandId ? "rgba(20,184,166,0.1)" : "transparent",
border: "none", borderRadius: 6, border: "none", borderRadius: 6,
cursor: "pointer", color: "#e8e9f0", cursor: "pointer", color: "#e8e9f0",
textAlign: "left", fontFamily: "inherit", textAlign: "left", fontFamily: "inherit",
@@ -118,14 +118,14 @@ export function TopBandBar() {
onMouseEnter={(e) => { if (band.id !== currentBandId) e.currentTarget.style.background = "rgba(255,255,255,0.04)"; }} onMouseEnter={(e) => { if (band.id !== currentBandId) e.currentTarget.style.background = "rgba(255,255,255,0.04)"; }}
onMouseLeave={(e) => { if (band.id !== currentBandId) e.currentTarget.style.background = "transparent"; }} onMouseLeave={(e) => { if (band.id !== currentBandId) e.currentTarget.style.background = "transparent"; }}
> >
<div style={{ width: 22, height: 22, borderRadius: 6, background: "rgba(139,92,246,0.15)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 9, fontWeight: 700, color: "#a78bfa", flexShrink: 0 }}> <div style={{ width: 22, height: 22, borderRadius: 6, background: "rgba(20,184,166,0.15)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 9, fontWeight: 700, color: "#2dd4bf", flexShrink: 0 }}>
{getInitials(band.name)} {getInitials(band.name)}
</div> </div>
<span style={{ flex: 1, fontSize: 12, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}> <span style={{ flex: 1, fontSize: 12, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
{band.name} {band.name}
</span> </span>
{band.id === currentBandId && ( {band.id === currentBandId && (
<span style={{ fontSize: 10, color: "#a78bfa", flexShrink: 0 }}></span> <span style={{ fontSize: 10, color: "#2dd4bf", flexShrink: 0 }}></span>
)} )}
</button> </button>
))} ))}

View File

@@ -16,11 +16,11 @@ input, textarea, button, select {
/* ── Design system (dark only — no light mode in v1) ─────────────────────── */ /* ── Design system (dark only — no light mode in v1) ─────────────────────── */
:root { :root {
/* v2 dark-space palette */ /* v2 dark-teal palette */
--bg: #0c0e1a; --bg: #080f0d;
--bg-card: #10131f; --bg-card: #0c1612;
--bg-raised: #151828; --bg-raised: #101c18;
--bg-hover: #1a1e30; --bg-hover: #142420;
--bg-subtle: rgba(255,255,255,0.025); --bg-subtle: rgba(255,255,255,0.025);
--bg-inset: rgba(255,255,255,0.04); --bg-inset: rgba(255,255,255,0.04);
--border: rgba(255,255,255,0.06); --border: rgba(255,255,255,0.06);
@@ -29,12 +29,12 @@ input, textarea, button, select {
--text: #e8e9f0; --text: #e8e9f0;
--text-muted: rgba(232,233,240,0.55); --text-muted: rgba(232,233,240,0.55);
--text-subtle: rgba(232,233,240,0.28); --text-subtle: rgba(232,233,240,0.28);
/* Violet accent */ /* Teal accent */
--accent: #8b5cf6; --accent: #14b8a6;
--accent-light: #a78bfa; --accent-light: #2dd4bf;
--accent-hover: #9f70f8; --accent-hover: #10a89a;
--accent-bg: rgba(139,92,246,0.12); --accent-bg: rgba(20,184,166,0.12);
--accent-border: rgba(139,92,246,0.3); --accent-border: rgba(20,184,166,0.3);
--accent-fg: #ffffff; --accent-fg: #ffffff;
--teal: #34d399; --teal: #34d399;
--teal-bg: rgba(52,211,153,0.1); --teal-bg: rgba(52,211,153,0.1);
@@ -53,7 +53,7 @@ input, textarea, button, select {
/* Bottom Navigation Bar */ /* Bottom Navigation Bar */
nav[style*="position: fixed"] { nav[style*="position: fixed"] {
display: flex; display: flex;
background: #0b0b0e; background: #060d0b;
border-top: 1px solid rgba(255, 255, 255, 0.06); border-top: 1px solid rgba(255, 255, 255, 0.06);
padding: 8px 16px; padding: 8px 16px;
} }

View File

@@ -95,8 +95,8 @@ function Input(props: React.InputHTMLAttributes<HTMLInputElement>) {
onBlur={(e) => { setFocused(false); props.onBlur?.(e); }} onBlur={(e) => { setFocused(false); props.onBlur?.(e); }}
style={{ style={{
width: "100%", padding: "8px 12px", width: "100%", padding: "8px 12px",
background: "#151828", background: "#101c18",
border: `1px solid ${focused ? "rgba(139,92,246,0.4)" : border}`, border: `1px solid ${focused ? "rgba(20,184,166,0.4)" : border}`,
borderRadius: 8, color: "#e8e9f0", borderRadius: 8, color: "#e8e9f0",
fontSize: 13, fontFamily: "inherit", fontSize: 13, fontFamily: "inherit",
outline: "none", boxSizing: "border-box", outline: "none", boxSizing: "border-box",
@@ -114,13 +114,13 @@ function SaveBtn({ pending, saved, onClick }: { pending: boolean; saved: boolean
disabled={pending} disabled={pending}
style={{ style={{
padding: "8px 20px", borderRadius: 8, padding: "8px 20px", borderRadius: 8,
background: saved ? "rgba(52,211,153,0.12)" : "linear-gradient(135deg, #7b5cf6, #06b6d4)", background: saved ? "rgba(52,211,153,0.12)" : "linear-gradient(135deg, #0d9488, #06b6d4)",
border: saved ? "1px solid rgba(52,211,153,0.3)" : "none", border: saved ? "1px solid rgba(52,211,153,0.3)" : "none",
color: saved ? "#34d399" : "white", color: saved ? "#34d399" : "white",
cursor: pending ? "default" : "pointer", cursor: pending ? "default" : "pointer",
fontSize: 13, fontWeight: 600, fontFamily: "inherit", fontSize: 13, fontWeight: 600, fontFamily: "inherit",
transition: "all 0.2s", transition: "all 0.2s",
boxShadow: saved ? "none" : "0 2px 12px rgba(139,92,246,0.3)", boxShadow: saved ? "none" : "0 2px 12px rgba(20,184,166,0.3)",
}} }}
> >
{pending ? "Saving…" : saved ? "Saved ✓" : "Save"} {pending ? "Saving…" : saved ? "Saved ✓" : "Save"}
@@ -211,7 +211,7 @@ function ProfileSection({ me }: { me: MemberRead }) {
} finally { setUploading(false); } } finally { setUploading(false); }
}} }}
/> />
<label htmlFor="avatar-upload" style={{ padding: "7px 14px", borderRadius: 8, background: "rgba(139,92,246,0.12)", border: "1px solid rgba(139,92,246,0.3)", color: "#a78bfa", cursor: "pointer", fontSize: 12, fontWeight: 600 }}> <label htmlFor="avatar-upload" style={{ padding: "7px 14px", borderRadius: 8, background: "rgba(20,184,166,0.12)", border: "1px solid rgba(20,184,166,0.3)", color: "#2dd4bf", cursor: "pointer", fontSize: 12, fontWeight: 600 }}>
{uploading ? "Uploading…" : "Upload"} {uploading ? "Uploading…" : "Upload"}
</label> </label>
<button <button
@@ -421,7 +421,7 @@ function StorageSection({ bandId, band, amAdmin, me }: { bandId: string; band: B
<Input value={folderInput} onChange={(e) => setFolderInput(e.target.value)} placeholder={defaultPath} style={{ fontFamily: "monospace" }} /> <Input value={folderInput} onChange={(e) => setFolderInput(e.target.value)} placeholder={defaultPath} style={{ fontFamily: "monospace" }} />
<div style={{ display: "flex", gap: 8, marginTop: 8 }}> <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
<button onClick={() => pathMutation.mutate(folderInput)} disabled={pathMutation.isPending} <button onClick={() => pathMutation.mutate(folderInput)} disabled={pathMutation.isPending}
style={{ padding: "6px 14px", background: "rgba(139,92,246,0.12)", border: "1px solid rgba(139,92,246,0.3)", borderRadius: 6, color: "#a78bfa", cursor: "pointer", fontSize: 12, fontWeight: 600, fontFamily: "inherit" }}> style={{ padding: "6px 14px", background: "rgba(20,184,166,0.12)", border: "1px solid rgba(20,184,166,0.3)", borderRadius: 6, color: "#2dd4bf", cursor: "pointer", fontSize: 12, fontWeight: 600, fontFamily: "inherit" }}>
{pathMutation.isPending ? "Saving…" : "Save"} {pathMutation.isPending ? "Saving…" : "Save"}
</button> </button>
<button onClick={() => setEditingPath(false)} <button onClick={() => setEditingPath(false)}
@@ -498,16 +498,16 @@ function MembersSection({ bandId, band, amAdmin, members, membersLoading }: { ba
<div style={{ marginBottom: 16 }}> <div style={{ marginBottom: 16 }}>
<button <button
onClick={() => inviteMutation.mutate()} disabled={inviteMutation.isPending} onClick={() => inviteMutation.mutate()} disabled={inviteMutation.isPending}
style={{ padding: "8px 16px", background: "rgba(139,92,246,0.12)", border: "1px solid rgba(139,92,246,0.3)", borderRadius: 8, color: "#a78bfa", cursor: "pointer", fontSize: 13, fontWeight: 600, fontFamily: "inherit" }}> style={{ padding: "8px 16px", background: "rgba(20,184,166,0.12)", border: "1px solid rgba(20,184,166,0.3)", borderRadius: 8, color: "#2dd4bf", cursor: "pointer", fontSize: 13, fontWeight: 600, fontFamily: "inherit" }}>
{inviteMutation.isPending ? "Generating…" : "+ Generate invite link"} {inviteMutation.isPending ? "Generating…" : "+ Generate invite link"}
</button> </button>
</div> </div>
)} )}
{inviteLink && ( {inviteLink && (
<div style={{ background: "rgba(139,92,246,0.06)", border: "1px solid rgba(139,92,246,0.22)", borderRadius: 10, padding: "12px 16px", marginBottom: 16 }}> <div style={{ background: "rgba(20,184,166,0.06)", border: "1px solid rgba(20,184,166,0.22)", borderRadius: 10, padding: "12px 16px", marginBottom: 16 }}>
<p style={{ color: "rgba(232,233,240,0.35)", fontSize: 11, margin: "0 0 5px" }}>Invite link (copied · valid 72h):</p> <p style={{ color: "rgba(232,233,240,0.35)", fontSize: 11, margin: "0 0 5px" }}>Invite link (copied · valid 72h):</p>
<code style={{ color: "#a78bfa", fontSize: 12, wordBreak: "break-all", fontFamily: "monospace" }}>{inviteLink}</code> <code style={{ color: "#2dd4bf", fontSize: 12, wordBreak: "break-all", fontFamily: "monospace" }}>{inviteLink}</code>
<button onClick={() => setInviteLink(null)} style={{ display: "block", marginTop: 6, background: "none", border: "none", color: "rgba(232,233,240,0.28)", cursor: "pointer", fontSize: 11, padding: 0, fontFamily: "inherit" }}> <button onClick={() => setInviteLink(null)} style={{ display: "block", marginTop: 6, background: "none", border: "none", color: "rgba(232,233,240,0.28)", cursor: "pointer", fontSize: 11, padding: 0, fontFamily: "inherit" }}>
Dismiss Dismiss
</button> </button>
@@ -528,7 +528,7 @@ function MembersSection({ bandId, band, amAdmin, members, membersLoading }: { ba
<div style={{ fontSize: 13, color: "rgba(232,233,240,0.75)" }}>{m.display_name}</div> <div style={{ fontSize: 13, color: "rgba(232,233,240,0.75)" }}>{m.display_name}</div>
<div style={{ fontSize: 11, color: "rgba(232,233,240,0.28)", marginTop: 1 }}>{m.email}</div> <div style={{ fontSize: 11, color: "rgba(232,233,240,0.28)", marginTop: 1 }}>{m.email}</div>
</div> </div>
<span style={{ fontSize: 10, fontFamily: "monospace", padding: "2px 7px", borderRadius: 4, background: m.role === "admin" ? "rgba(139,92,246,0.1)" : "rgba(255,255,255,0.05)", color: m.role === "admin" ? "#a78bfa" : "rgba(232,233,240,0.38)", border: `1px solid ${m.role === "admin" ? "rgba(139,92,246,0.28)" : border}`, whiteSpace: "nowrap" }}> <span style={{ fontSize: 10, fontFamily: "monospace", padding: "2px 7px", borderRadius: 4, background: m.role === "admin" ? "rgba(20,184,166,0.1)" : "rgba(255,255,255,0.05)", color: m.role === "admin" ? "#2dd4bf" : "rgba(232,233,240,0.38)", border: `1px solid ${m.role === "admin" ? "rgba(20,184,166,0.28)" : border}`, whiteSpace: "nowrap" }}>
{m.role} {m.role}
</span> </span>
{amAdmin && m.role !== "admin" && ( {amAdmin && m.role !== "admin" && (
@@ -545,7 +545,7 @@ function MembersSection({ bandId, band, amAdmin, members, membersLoading }: { ba
{/* Role legend */} {/* Role legend */}
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginBottom: amAdmin ? 0 : 0 }}> <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginBottom: amAdmin ? 0 : 0 }}>
<div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 8 }}> <div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 8 }}>
<div style={{ fontSize: 12, color: "#a78bfa", marginBottom: 3 }}>Admin</div> <div style={{ fontSize: 12, color: "#2dd4bf", marginBottom: 3 }}>Admin</div>
<div style={{ fontSize: 11, color: "rgba(232,233,240,0.28)", lineHeight: 1.55 }}>Upload, delete, manage members and storage</div> <div style={{ fontSize: 11, color: "rgba(232,233,240,0.28)", lineHeight: 1.55 }}>Upload, delete, manage members and storage</div>
</div> </div>
<div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 8 }}> <div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 8 }}>
@@ -627,9 +627,9 @@ function BandSection({ bandId, band }: { bandId: string; band: Band }) {
<Label>Genre tags</Label> <Label>Genre tags</Label>
<div style={{ display: "flex", gap: 5, flexWrap: "wrap", marginBottom: 8 }}> <div style={{ display: "flex", gap: 5, flexWrap: "wrap", marginBottom: 8 }}>
{tags.map((t) => ( {tags.map((t) => (
<span key={t} style={{ background: "rgba(139,92,246,0.1)", color: "#a78bfa", fontSize: 11, padding: "3px 10px", borderRadius: 20, display: "flex", alignItems: "center", gap: 5 }}> <span key={t} style={{ background: "rgba(20,184,166,0.1)", color: "#2dd4bf", fontSize: 11, padding: "3px 10px", borderRadius: 20, display: "flex", alignItems: "center", gap: 5 }}>
{t} {t}
<button onClick={() => setTags((p) => p.filter((x) => x !== t))} style={{ background: "none", border: "none", color: "#a78bfa", cursor: "pointer", fontSize: 13, padding: 0, lineHeight: 1, fontFamily: "inherit" }}>×</button> <button onClick={() => setTags((p) => p.filter((x) => x !== t))} style={{ background: "none", border: "none", color: "#2dd4bf", cursor: "pointer", fontSize: 13, padding: 0, lineHeight: 1, fontFamily: "inherit" }}>×</button>
</span> </span>
))} ))}
</div> </div>
@@ -671,13 +671,13 @@ function NavItem({ label, active, onClick }: { label: string; active: boolean; o
padding: "8px 10px", borderRadius: 8, padding: "8px 10px", borderRadius: 8,
border: "none", cursor: "pointer", border: "none", cursor: "pointer",
fontSize: 13, fontFamily: "inherit", fontWeight: active ? 600 : 400, fontSize: 13, fontFamily: "inherit", fontWeight: active ? 600 : 400,
background: active ? "rgba(139,92,246,0.1)" : hovered ? "rgba(255,255,255,0.04)" : "transparent", background: active ? "rgba(20,184,166,0.1)" : hovered ? "rgba(255,255,255,0.04)" : "transparent",
color: active ? "#a78bfa" : hovered ? "rgba(232,233,240,0.75)" : "rgba(232,233,240,0.45)", color: active ? "#2dd4bf" : hovered ? "rgba(232,233,240,0.75)" : "rgba(232,233,240,0.45)",
transition: "all 0.12s", transition: "all 0.12s",
position: "relative", position: "relative",
}} }}
> >
{active && <div style={{ position: "absolute", left: 0, top: "20%", bottom: "20%", width: 2, borderRadius: "0 2px 2px 0", background: "linear-gradient(to bottom, #7b5cf6, #22d3ee)" }} />} {active && <div style={{ position: "absolute", left: 0, top: "20%", bottom: "20%", width: 2, borderRadius: "0 2px 2px 0", background: "linear-gradient(to bottom, #0d9488, #22d3ee)" }} />}
{label} {label}
</button> </button>
); );
@@ -744,7 +744,7 @@ export function SettingsPage() {
{section !== "profile" || searchParams.has("section") ? ( {section !== "profile" || searchParams.has("section") ? (
<div> <div>
<button onClick={() => setSearchParams({})} <button onClick={() => setSearchParams({})}
style={{ display: "flex", alignItems: "center", gap: 6, padding: "12px 16px", background: "none", border: "none", cursor: "pointer", color: "#a78bfa", fontSize: 13, fontFamily: "inherit" }}> style={{ display: "flex", alignItems: "center", gap: 6, padding: "12px 16px", background: "none", border: "none", cursor: "pointer", color: "#2dd4bf", fontSize: 13, fontFamily: "inherit" }}>
Settings Settings
</button> </button>
<div style={{ padding: "0 16px 24px" }}> <div style={{ padding: "0 16px 24px" }}>
@@ -782,7 +782,7 @@ export function SettingsPage() {
<div style={{ display: "flex", height: "100%", overflow: "hidden" }}> <div style={{ display: "flex", height: "100%", overflow: "hidden" }}>
{/* Left nav */} {/* Left nav */}
<nav style={{ width: 220, minWidth: 220, background: "#10131f", borderRight: `1px solid ${border}`, padding: "20px 12px", display: "flex", flexDirection: "column", overflow: "hidden", flexShrink: 0 }}> <nav style={{ width: 220, minWidth: 220, background: "#0c1612", borderRight: `1px solid ${border}`, padding: "20px 12px", display: "flex", flexDirection: "column", overflow: "hidden", flexShrink: 0 }}>
<h1 style={{ fontSize: 16, fontWeight: 800, color: "#e8e9f0", margin: "0 0 4px 8px", letterSpacing: -0.3 }}>Settings</h1> <h1 style={{ fontSize: 16, fontWeight: 800, color: "#e8e9f0", margin: "0 0 4px 8px", letterSpacing: -0.3 }}>Settings</h1>
<div style={{ height: 1, background: border, margin: "12px 0" }} /> <div style={{ height: 1, background: border, margin: "12px 0" }} />
@@ -803,7 +803,7 @@ export function SettingsPage() {
// Stay on same section if possible // Stay on same section if possible
setSearchParams({ section }, { replace: true }); setSearchParams({ section }, { replace: true });
}} }}
style={{ width: "100%", padding: "6px 8px", background: "#151828", border: `1px solid ${borderBright}`, borderRadius: 7, color: "#e8e9f0", fontSize: 12, fontFamily: "inherit", cursor: "pointer", outline: "none" }} style={{ width: "100%", padding: "6px 8px", background: "#101c18", border: `1px solid ${borderBright}`, borderRadius: 7, color: "#e8e9f0", fontSize: 12, fontFamily: "inherit", cursor: "pointer", outline: "none" }}
> >
{bands?.map((b) => ( {bands?.map((b) => (
<option key={b.id} value={b.id}>{b.name}</option> <option key={b.id} value={b.id}>{b.name}</option>