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:
parent
1062e003bf
commit
4db33399e9
5 changed files with 173 additions and 19 deletions
|
|
@ -25,7 +25,7 @@ Update existing test `test_personality_prompt_injected_when_weight_and_profile`
|
|||
- Estimate: 45m
|
||||
- Files: backend/chat_service.py, backend/tests/test_chat.py
|
||||
- Verify: cd backend && python -m pytest tests/test_chat.py -v -k personality
|
||||
- [ ] **T02: Enhance slider UX with dynamic tier label, value indicator, and gradient track** — Enhance the personality slider in ChatWidget.tsx with three visual feedback features:
|
||||
- [x] **T02: Added getTierLabel() helper, gradient track fill via --slider-fill CSS custom property, and centered tier label + numeric value below the personality slider** — Enhance the personality slider in ChatWidget.tsx with three visual feedback features:
|
||||
|
||||
1. **Dynamic tier label**: Below the slider, show a centered label that changes based on current weight value:
|
||||
- 0.0–0.19: "Encyclopedic"
|
||||
|
|
|
|||
24
.gsd/milestones/M023/slices/S04/tasks/T01-VERIFY.json
Normal file
24
.gsd/milestones/M023/slices/S04/tasks/T01-VERIFY.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"taskId": "T01",
|
||||
"unitId": "M023/S04/T01",
|
||||
"timestamp": 1775297087024,
|
||||
"passed": false,
|
||||
"discoverySource": "task-plan",
|
||||
"checks": [
|
||||
{
|
||||
"command": "cd backend",
|
||||
"exitCode": 0,
|
||||
"durationMs": 11,
|
||||
"verdict": "pass"
|
||||
},
|
||||
{
|
||||
"command": "python -m pytest tests/test_chat.py -v -k personality",
|
||||
"exitCode": 4,
|
||||
"durationMs": 264,
|
||||
"verdict": "fail"
|
||||
}
|
||||
],
|
||||
"retryAttempt": 1,
|
||||
"maxRetries": 2
|
||||
}
|
||||
79
.gsd/milestones/M023/slices/S04/tasks/T02-SUMMARY.md
Normal file
79
.gsd/milestones/M023/slices/S04/tasks/T02-SUMMARY.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
id: T02
|
||||
parent: S04
|
||||
milestone: M023
|
||||
provides: []
|
||||
requires: []
|
||||
affects: []
|
||||
key_files: ["frontend/src/components/ChatWidget.tsx", "frontend/src/components/ChatWidget.module.css"]
|
||||
key_decisions: ["Wrapped sliderRow in sliderSection container for clean grouping", "Used CSS custom property --slider-fill via inline style for gradient track", "Tabular-nums on tier value for stable width"]
|
||||
patterns_established: []
|
||||
drill_down_paths: []
|
||||
observability_surfaces: []
|
||||
duration: ""
|
||||
verification_result: "Frontend build passes clean (cd frontend && npm run build, exit 0). Backend personality tests all pass (cd backend && python -m pytest tests/test_chat.py -v -k personality, 11/11 passed)."
|
||||
completed_at: 2026-04-04T10:06:38.357Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# T02: Added getTierLabel() helper, gradient track fill via --slider-fill CSS custom property, and centered tier label + numeric value below the personality slider
|
||||
|
||||
> Added getTierLabel() helper, gradient track fill via --slider-fill CSS custom property, and centered tier label + numeric value below the personality slider
|
||||
|
||||
## What Happened
|
||||
---
|
||||
id: T02
|
||||
parent: S04
|
||||
milestone: M023
|
||||
key_files:
|
||||
- frontend/src/components/ChatWidget.tsx
|
||||
- frontend/src/components/ChatWidget.module.css
|
||||
key_decisions:
|
||||
- Wrapped sliderRow in sliderSection container for clean grouping
|
||||
- Used CSS custom property --slider-fill via inline style for gradient track
|
||||
- Tabular-nums on tier value for stable width
|
||||
duration: ""
|
||||
verification_result: passed
|
||||
completed_at: 2026-04-04T10:06:38.357Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# T02: Added getTierLabel() helper, gradient track fill via --slider-fill CSS custom property, and centered tier label + numeric value below the personality slider
|
||||
|
||||
**Added getTierLabel() helper, gradient track fill via --slider-fill CSS custom property, and centered tier label + numeric value below the personality slider**
|
||||
|
||||
## What Happened
|
||||
|
||||
Three visual feedback enhancements to the personality slider: getTierLabel() maps weight ranges to 5 tier labels matching the backend interpolation tiers; gradient track fill via --slider-fill CSS custom property on both webkit and moz pseudo-elements; centered tier info row with label and tabular-nums numeric value. Wrapped sliderRow in sliderSection container for clean grouping.
|
||||
|
||||
## Verification
|
||||
|
||||
Frontend build passes clean (cd frontend && npm run build, exit 0). Backend personality tests all pass (cd backend && python -m pytest tests/test_chat.py -v -k personality, 11/11 passed).
|
||||
|
||||
## Verification Evidence
|
||||
|
||||
| # | Command | Exit Code | Verdict | Duration |
|
||||
|---|---------|-----------|---------|----------|
|
||||
| 1 | `cd frontend && npm run build` | 0 | ✅ pass | 6800ms |
|
||||
| 2 | `cd backend && python -m pytest tests/test_chat.py -v -k personality` | 0 | ✅ pass | 3900ms |
|
||||
|
||||
|
||||
## Deviations
|
||||
|
||||
Added .sliderSection wrapper div to group slider row and tier info under one border-bottom — minor structural improvement not in original plan.
|
||||
|
||||
## Known Issues
|
||||
|
||||
None.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `frontend/src/components/ChatWidget.tsx`
|
||||
- `frontend/src/components/ChatWidget.module.css`
|
||||
|
||||
|
||||
## Deviations
|
||||
Added .sliderSection wrapper div to group slider row and tier info under one border-bottom — minor structural improvement not in original plan.
|
||||
|
||||
## Known Issues
|
||||
None.
|
||||
|
|
@ -69,13 +69,16 @@
|
|||
|
||||
/* ── Personality slider ───────────────────────────────────── */
|
||||
|
||||
.sliderSection {
|
||||
padding: 0.5rem 1rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sliderRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sliderLabel {
|
||||
|
|
@ -91,7 +94,11 @@
|
|||
appearance: none;
|
||||
height: 4px;
|
||||
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;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -126,7 +133,35 @@
|
|||
.slider::-moz-range-track {
|
||||
height: 4px;
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -115,6 +115,15 @@ function parseCitations(text: string, sources: ChatSource[]): React.ReactNode[]
|
|||
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) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
|
|
@ -275,6 +284,7 @@ export default function ChatWidget({ creatorName, techniques }: ChatWidgetProps)
|
|||
</div>
|
||||
|
||||
{/* Personality slider */}
|
||||
<div className={styles.sliderSection}>
|
||||
<div className={styles.sliderRow}>
|
||||
<span className={styles.sliderLabel}>Encyclopedic</span>
|
||||
<input
|
||||
|
|
@ -286,9 +296,15 @@ export default function ChatWidget({ creatorName, techniques }: ChatWidgetProps)
|
|||
value={personalityWeight}
|
||||
onChange={(e) => setPersonalityWeight(parseFloat(e.target.value))}
|
||||
aria-label="Personality weight"
|
||||
style={{ '--slider-fill': `${personalityWeight * 100}%` } as React.CSSProperties}
|
||||
/>
|
||||
<span className={styles.sliderLabel}>Creator Voice</span>
|
||||
</div>
|
||||
<div className={styles.tierInfo}>
|
||||
<span className={styles.tierLabel}>{getTierLabel(personalityWeight)}</span>
|
||||
<span className={styles.tierValue}>{personalityWeight.toFixed(1)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Messages */}
|
||||
<div className={styles.messages}>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue