from html import escape from dream_customs.schema import CustomsSession, EvidenceItem, PactCard, TimelineEvent, TodayTipCard def _nl2br(text: str) -> str: return "
".join(escape(text).splitlines()) def _phase_label(phase: str) -> str: labels = { "empty": "Empty desk", "record": "Record", "ask": "Ask", "interpret": "Interpret", "tip": "Today Tip", "declaring": "Declare", "negotiating": "Negotiate", "drafting": "Draft", "sealed": "Sealed", "error": "Needs attention", } return labels.get(phase, phase.title()) def render_today_tip_card(card: TodayTipCard, language: str = "en") -> str: is_zh = language == "zh" anchors = " ".join(escape(anchor) for anchor in card.dream_anchors) questions = "".join(f"
  • {escape(question)}
  • " for question in card.followup_questions) answers = "".join(f"
  • {escape(answer)}
  • " for answer in card.user_answers) labels = { "page": "今日小 Tips" if is_zh else "Today Tip", "thanks": "谢谢你的分享。根据你的梦境,我们为你整理了今天的参考。" if is_zh else "Thanks for sharing this. Here is one grounded suggestion for today.", "interpretation": "补充解读" if is_zh else "Supporting reflection", "today_tip": "今天的小建议" if is_zh else "Today's small suggestion", "tiny_action": "没试过的小事" if is_zh else "Tiny action", "anchors": "关键词:" if is_zh else "Anchors:", "history": "追问记录" if is_zh else "Question record", "safety_default": "这不是诊断,只是一个温和的今日参考。" if is_zh else "A gentle reflection for today, not a diagnosis or prophecy.", "safety_prefix": "这不是诊断。" if is_zh else "This is not a diagnosis. ", "highlight": "梦境细节" if is_zh else "dream detail", } tiny_action = ( f"

    {labels['tiny_action']}

    {escape(card.tiny_action)}

    " if card.tiny_action else "" ) caring_note = ( f"
    ♡ {escape(card.caring_note)}
    " if card.caring_note else "" ) safety = ( f"
    {labels['safety_prefix']}{escape(card.safety_note)}
    " if card.safety_note else f"
    {labels['safety_default']}
    " ) qa_history = ( f"
    {labels['history']}
      {questions}
    " if questions or answers else "" ) return f"""

    {labels['page']}

    {labels['thanks']}

    {labels['today_tip']}

    {escape(card.today_tip)}

    ★ {escape(card.dream_anchors[0] if card.dream_anchors else labels['highlight'])}
    {tiny_action}

    {labels['interpretation']}

    {escape(card.interpretation)}

    {labels['anchors']} {anchors}
    {qa_history} {safety} {caring_note}
    """.strip() def _evidence_chip(item: EvidenceItem) -> str: return ( f"" f"{escape(item.label)}" ) def render_status_bar(session: CustomsSession, text_backend: str = "demo", vision_backend: str = "demo") -> str: safety = "Support note ready" if session.safety_flags else "Playful reflection, not diagnosis" return f"""

    梦境问答台

    记录梦境,回答或跳过温和追问,得到一个引用梦境细节的今日小 Tips。

    Current: {escape(_phase_label(session.phase))} Text {escape(text_backend or "demo")} Vision {escape(vision_backend or "demo")} {escape(safety)}
    """.strip() def _render_event(event: TimelineEvent) -> str: body = f"

    {_nl2br(event.body)}

    " if event.body else "" meta = f"{escape(event.meta)}" if event.meta else "" status = f"{escape(event.status)}" if event.status else "" return f"""
    {meta}{status}

    {escape(event.title)}

    {body}
    """.strip() def render_timeline(session: CustomsSession) -> str: if not session.events: events = [ TimelineEvent( role="system", title="梦境问答台已打开", body="先写下一个梦境片段。文字、图片、语音都会进入同一个 intake。", status="ready", ) ] else: events = session.events evidence = "".join(_evidence_chip(item) for item in session.evidence_items[-8:]) evidence_tray = ( f"
    {evidence}
    " if evidence else ( "
    " "Text" "Image" "Voice" "Mood" "
    " ) ) return f"""
    梦境问答流程

    目前记录

    梦境片段、追问、回答和解读草稿会按时间线留在这里。

    {session.evidence_count()} filed
    {evidence_tray}
    {''.join(_render_event(event) for event in events)}
    """.strip() def render_pact_inspector(session: CustomsSession) -> str: if session.sealed_tip or session.draft_tip: return render_today_tip_card(session.sealed_tip or session.draft_tip) card = session.sealed_pact or session.draft_pact state = "sealed" if session.sealed_pact else "draft" if session.draft_pact else "waiting" if not card: return f""" """.strip() contraband = "".join(f"
  • {escape(item)}
  • " for item in card.contraband) safety = ( f"
    Support note

    {escape(card.safety_note)}

    " if card.safety_note else "" ) return f""" """.strip() def render_pact_card(card: PactCard) -> str: contraband = "".join(f"
  • {escape(item)}
  • " for item in card.contraband) safety = ( f"
    Safety note

    {escape(card.safety_note)}

    " if card.safety_note else "" ) return f"""
    Dream Customs {escape(card.permit_id)}

    Today's Pact

    {escape(card.visitor_name)}

    Risk: {escape(card.risk_level)}

    Alliance: {escape(card.alliance_reading)}

    Life tip: {escape(card.practical_suggestion)}

    Weird 5-minute task: {escape(card.weird_task)}

    Bedtime release: {escape(card.bedtime_release)}

    Contraband:
    {safety} SEALED
    """.strip()