Viney Claude Sonnet 4.6 commited on
Commit
402936a
Β·
1 Parent(s): 77b9e55

feat: add subtext_card component for between_the_lines display

Browse files

Adds _SIGNAL_TYPE_LABELS (FR labels + indigo/red/amber/gray/sky palette)
and subtext_card(item) rendering observation β†’ reading β†’ implication with
signal-type chip, AI badge, collapsible evidence quote, and meta row.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. dashboard/components.py +92 -0
dashboard/components.py CHANGED
@@ -575,3 +575,95 @@ def delta_card(delta: dict) -> None:
575
  f'</div>',
576
  unsafe_allow_html=True,
577
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  f'</div>',
576
  unsafe_allow_html=True,
577
  )
578
+
579
+
580
+ # ── Between-the-lines / SubtextRead card ─────────────────────────────────────
581
+
582
+ _SIGNAL_TYPE_LABELS: dict[str, tuple[str, str, str]] = {
583
+ # signal_type β†’ (label_fr, fg_color, bg_color)
584
+ "language_drift": ("DΓ©rive de langage", "#4f46e5", "#eef2ff"), # indigo
585
+ "qa_evasion": ("Γ‰vasion en Q&A", "#dc2626", "#fef2f2"), # red
586
+ "omission": ("Omission", "#d97706", "#fffbeb"), # amber
587
+ "emphasis_shift": ("DΓ©placement d'emphase", "#6b7280", "#f3f4f6"), # gray
588
+ "accounting_quality": ("QualitΓ© comptable", "#0ea5e9", "#f0f9ff"), # sky
589
+ }
590
+
591
+
592
+ def subtext_card(item: dict) -> None:
593
+ """Render a SubtextRead observation as a structured card in Streamlit.
594
+
595
+ Displays: signal-type chip + AI badge, observation, expert reading,
596
+ implication (Γ  surveiller), and collapsible evidence quote with meta row.
597
+ """
598
+ observation = item.get("observation", "")
599
+ reading = item.get("reading", "")
600
+ signal_type = item.get("signal_type", "")
601
+ implication = item.get("implication", "")
602
+ evidence = item.get("evidence") or {}
603
+
604
+ # Resolve signal type styling; fall back to AMBER/WARN_BG for unknowns
605
+ if signal_type in _SIGNAL_TYPE_LABELS:
606
+ sig_label, fg_color, sig_bg = _SIGNAL_TYPE_LABELS[signal_type]
607
+ else:
608
+ sig_label, fg_color, sig_bg = "Signal", AMBER, WARN_BG
609
+
610
+ # Signal type chip β€” pill style matching tension_card's weight_chip
611
+ signal_chip = (
612
+ f'<span style="background:{sig_bg};color:{fg_color};border:1px solid {fg_color}33;'
613
+ f'border-radius:4px;padding:1px 7px;font-size:0.65rem;font-weight:600;'
614
+ f'text-transform:uppercase;letter-spacing:0.06em;">{sig_label}</span>'
615
+ )
616
+
617
+ # Evidence fields
618
+ ev_snippet = evidence.get("evidence_snippet", "") if isinstance(evidence, dict) else ""
619
+ ev_rel = evidence.get("reliability", "") if isinstance(evidence, dict) else ""
620
+ ev_src = evidence.get("source", "") if isinstance(evidence, dict) else ""
621
+ ev_imp = (evidence.get("impact", "") or "") if isinstance(evidence, dict) else ""
622
+
623
+ st.markdown(
624
+ f'<div class="primer-card" style="background:{BG};border:1px solid {BORDER};'
625
+ f'border-left:4px solid {fg_color};border-radius:10px;'
626
+ f'padding:16px 18px;margin-bottom:10px;">'
627
+
628
+ # ── Top row: chip left, AI badge right ────────────────────────────
629
+ f'<div style="display:flex;align-items:center;justify-content:space-between;'
630
+ f'margin-bottom:12px;">'
631
+ f'{signal_chip}'
632
+ f'{ai_badge("entre les lignes")}'
633
+ f'</div>'
634
+
635
+ # ── OBSERVATION ────────────────────────────────────────────────────
636
+ f'<div style="margin-bottom:10px;">'
637
+ f'{eyebrow_label("Observation", TEXT_MUTED)}'
638
+ f'<div style="font-size:{FS_BODY};color:{TEXT};line-height:1.55;margin-top:4px;">'
639
+ f'{observation}'
640
+ f'</div>'
641
+ f'</div>'
642
+
643
+ # ── LECTURE EXPERTE ────────────────────────────────────────────────
644
+ f'<div style="margin-bottom:12px;">'
645
+ f'{eyebrow_label("Lecture experte", fg_color)}'
646
+ f'<div style="font-size:{FS_BODY};font-style:italic;color:{TEXT};'
647
+ f'line-height:1.55;margin-top:4px;">'
648
+ f'{reading}'
649
+ f'</div>'
650
+ f'</div>'
651
+
652
+ # ── Separator ──────────────────────────────────────────────────────
653
+ f'<div style="border-top:1px solid {BORDER};margin-bottom:12px;"></div>'
654
+
655
+ # ── Γ€ SURVEILLER ──────────────────────────────────────────────────
656
+ f'<div style="margin-bottom:10px;">'
657
+ f'{eyebrow_label("Γ€ surveiller", AMBER)}'
658
+ f'<div style="font-size:{FS_BODY};color:{TEXT_MUTED};line-height:1.55;margin-top:4px;">'
659
+ f'{implication}'
660
+ f'</div>'
661
+ f'</div>'
662
+
663
+ # ── Evidence quote + meta row ──────────────────────────────────────
664
+ f'{evidence_quote(ev_snippet, bg=BG_MUTED, border_color=BORDER)}'
665
+ f'{meta_row(ev_rel, ev_src, ev_imp)}'
666
+
667
+ f'</div>',
668
+ unsafe_allow_html=True,
669
+ )