feat: replace arrowDown markers with ★ stars overlay on chart, colored by category

- InstrumentChart: remove setMarkers arrowDown (level-colored)
  Add DOM overlay div with ★ HTML characters positioned via timeToCoordinate +
  priceToCoordinate(candle.high) - 52px; sized by impact_score; colored by category
  Label (title, 22 chars) rendered above each star; redrawn on scroll/zoom via
  subscribeVisibleLogicalRangeChange; cleaned up on unmount
  ChartEvent interface: add category, impact_score, end_date, description
  Header legend: replace ▼ LT/MT/CT with ★ category color legend
- InstrumentDashboard: remove EventStarsStrip (now internal to chart)
  Remove unused dateToMs helper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-25 17:24:33 +02:00
parent 51a5531454
commit 1d433b4a7a
2 changed files with 101 additions and 90 deletions

View File

@@ -109,8 +109,6 @@ function fmtDateFR(s: string | null): string {
return `${d}/${m}/${y}`
}
function dateToMs(s: string) { return new Date(s).getTime() }
function pctN(a: number | undefined, b: number | undefined): number {
if (a === undefined || b === undefined || b === 0) return 0
return ((a - b) / b) * 100
@@ -507,63 +505,6 @@ function NarrativeCard({ narrative, loading, onLoad, instrument }: {
)
}
// ── EventStarsStrip — étoiles au-dessus du graphe ────────────────────────────
const CAT_STAR_COLORS: Record<string, string> = {
event_calendar: '#f59e0b', // amber
geopolitical: '#ef4444', // red
fundamental: '#10b981', // emerald
report: '#3b82f6', // blue
sentiment: '#8b5cf6', // violet
technical: '#06b6d4', // cyan
}
function EventStarsStrip({
events, priceData,
}: {
events: SnapshotEvent[]
priceData: PriceCandle[]
}) {
if (priceData.length < 2 || events.length === 0) return null
const chartStart = dateToMs(priceData[0].time)
const chartEnd = dateToMs(priceData[priceData.length - 1].time)
const totalMs = chartEnd - chartStart
if (totalMs <= 0) return null
function leftPct(d: string) {
return Math.max(0.5, Math.min(98.5, ((dateToMs(d) - chartStart) / totalMs) * 100))
}
return (
<div className="relative" style={{ height: 28, paddingRight: 62 }}>
{events.map((ev, i) => {
const left = leftPct(ev.date)
const color = CAT_STAR_COLORS[ev.category] ?? '#94a3b8'
const score = ev.impact_score ?? 0.5
const size = score >= 0.8 ? 16 : score >= 0.6 ? 13 : 11
return (
<span
key={i}
className="absolute -translate-x-1/2 select-none cursor-default transition-transform hover:scale-150"
style={{
left: `${left}%`,
bottom: 2,
fontSize: size,
color,
lineHeight: 1,
filter: `drop-shadow(0 0 3px ${color}60)`,
}}
title={`${ev.title}\n${ev.date}${ev.end_date ? ' → ' + ev.end_date : ''}\n${ev.description ?? ''}`}
>
</span>
)
})}
</div>
)
}
// ── DriversPanel — édition inline ────────────────────────────────────────────
function DriversPanel({ instrumentId, drivers, onSave, onClose }: {
@@ -915,11 +856,6 @@ export default function InstrumentDashboard() {
{/* ── Content ── */}
{!loading && snapshot && (
<>
<EventStarsStrip
events={snapshot.events}
priceData={snapshot.price_data}
/>
<InstrumentChart
priceData={snapshot.price_data}
indicators={snapshot.indicators}