feat: Added getTierLabel() helper, gradient track fill via --slider-fil…

- "frontend/src/components/ChatWidget.tsx"
- "frontend/src/components/ChatWidget.module.css"

GSD-Task: S04/T02
This commit is contained in:
jlightner 2026-04-04 10:06:46 +00:00
parent d32864de6a
commit 8e27f994db
2 changed files with 69 additions and 18 deletions

View file

@ -69,13 +69,16 @@
/* ── Personality slider ───────────────────────────────────── */ /* ── Personality slider ───────────────────────────────────── */
.sliderSection {
padding: 0.5rem 1rem;
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
}
.sliderRow { .sliderRow {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
padding: 0.5rem 1rem;
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
} }
.sliderLabel { .sliderLabel {
@ -91,7 +94,11 @@
appearance: none; appearance: none;
height: 4px; height: 4px;
border-radius: 2px; border-radius: 2px;
background: var(--color-border); background: linear-gradient(
to right,
var(--color-accent) var(--slider-fill, 0%),
var(--color-border) var(--slider-fill, 0%)
);
outline: none; outline: none;
cursor: pointer; cursor: pointer;
} }
@ -126,7 +133,35 @@
.slider::-moz-range-track { .slider::-moz-range-track {
height: 4px; height: 4px;
border-radius: 2px; border-radius: 2px;
background: var(--color-border); background: linear-gradient(
to right,
var(--color-accent) var(--slider-fill, 0%),
var(--color-border) var(--slider-fill, 0%)
);
}
/* ── Tier label + value ───────────────────────────────────── */
.tierInfo {
display: flex;
justify-content: center;
align-items: baseline;
gap: 0.375rem;
padding-top: 0.25rem;
}
.tierLabel {
font-size: 0.6875rem;
font-weight: 600;
color: var(--color-text-secondary);
transition: color 0.2s;
}
.tierValue {
font-size: 0.625rem;
color: var(--color-text-secondary);
opacity: 0.6;
font-variant-numeric: tabular-nums;
} }
.headerTitle { .headerTitle {

View file

@ -115,6 +115,15 @@ function parseCitations(text: string, sources: ChatSource[]): React.ReactNode[]
return nodes.length > 0 ? nodes : [text]; return nodes.length > 0 ? nodes : [text];
} }
/** Map personality weight to a human-readable tier label. */
function getTierLabel(weight: number): string {
if (weight < 0.2) return "Encyclopedic";
if (weight < 0.4) return "Subtle Reference";
if (weight < 0.6) return "Creator Tone";
if (weight < 0.8) return "Creator Voice";
return "Full Embodiment";
}
export default function ChatWidget({ creatorName, techniques }: ChatWidgetProps) { export default function ChatWidget({ creatorName, techniques }: ChatWidgetProps) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [messages, setMessages] = useState<Message[]>([]); const [messages, setMessages] = useState<Message[]>([]);
@ -275,6 +284,7 @@ export default function ChatWidget({ creatorName, techniques }: ChatWidgetProps)
</div> </div>
{/* Personality slider */} {/* Personality slider */}
<div className={styles.sliderSection}>
<div className={styles.sliderRow}> <div className={styles.sliderRow}>
<span className={styles.sliderLabel}>Encyclopedic</span> <span className={styles.sliderLabel}>Encyclopedic</span>
<input <input
@ -286,9 +296,15 @@ export default function ChatWidget({ creatorName, techniques }: ChatWidgetProps)
value={personalityWeight} value={personalityWeight}
onChange={(e) => setPersonalityWeight(parseFloat(e.target.value))} onChange={(e) => setPersonalityWeight(parseFloat(e.target.value))}
aria-label="Personality weight" aria-label="Personality weight"
style={{ '--slider-fill': `${personalityWeight * 100}%` } as React.CSSProperties}
/> />
<span className={styles.sliderLabel}>Creator Voice</span> <span className={styles.sliderLabel}>Creator Voice</span>
</div> </div>
<div className={styles.tierInfo}>
<span className={styles.tierLabel}>{getTierLabel(personalityWeight)}</span>
<span className={styles.tierValue}>{personalityWeight.toFixed(1)}</span>
</div>
</div>
{/* Messages */} {/* Messages */}
<div className={styles.messages}> <div className={styles.messages}>