CHAT_DIRECTIVE_PREVIEW_POLISH

Polish chat-preview rendering of raw Galaxy markdown directives

Summary

Agent chat replies that inline Galaxy directives (e.g. ```galaxy\nhistory_dataset_display(history_dataset_id=42)\n```) render as raw text in the chat panel — because chat uses a vanilla MarkdownIt engine, not the directive-hydrating renderer used by the page preview. Behavior is correct (chat should not hydrate directives), but the unstyled raw text is unpolished. Goal: keep chat non-hydrating, but present directive blocks/inline refs as readable chips/code with light affordances.

Current behavior

Why this is fine but unpolished

Hydrating directives inside chat would re-trigger dataset fetches per message, drag in heavy components for content the user has not asked to embed, and make scrollback re-render the world. The chat is correctly a transcript, not a published page. The problem is purely cosmetic: raw ```galaxy and ${galaxy ...} look like leaked internals, and embed-heavy proposals copy-pasted into prose can bloat exchanges. Polish, don’t hydrate.

STYLED_CHIPS_AND_FENCE_PILL: keep a non-hydrating MarkdownIt path, but add small post-processing so directive surfaces read as first-class UI:

Rationale: reuses the existing parse.ts directive-extraction logic conceptually without depending on its Vue hydration chain; keeps chat fast; matches the agent’s intent (“here is the directive I’d embed”) without lying about live rendering.

Alternatives considered

OptionOne-linerWhy rejected
STYLED_CHIPS_AND_FENCE_PILLPost-process MarkdownIt output to chip directives, no hydration.Recommended.
FULL_HYDRATION_IN_CHATRun chat content through Markdown.vue / SectionWrapper.Triggers per-message dataset/job fetches; heavy DOM in transcript; defeats purpose of chat-as-log; risks reactive loops if message list rerenders.
PROMPT_ONLY_FIXTell agent in page_assistant.md never to emit directives in chat prose, only in replace_entire_document / patch_section.Necessary-but-not-sufficient — prompt is already strict (lib/galaxy/agents/prompts/page_assistant.md:31, :55) and agent will still cite directives when explaining (“here’s the directive I used: …”). Pair this with STYLED_CHIPS rather than rely on it alone. Worth a small tweak (see Implementation).
STRIP_DIRECTIVES_FROM_CHATPre-process to remove ```galaxy blocks from chat content before render.Hides information from the user; breaks “how does directive X work?” Q&A use case explicitly called out in prompt (:33).
HYDRATE_ONLY_NAME_DIRECTIVESHydrate only cheap inline directives (history_dataset_name).Inconsistent surface; still triggers small fetches per chat scroll; not worth complexity over chips.

Implementation plan

  1. Add a TS module client/src/components/ChatGXY/galaxyDirectivePreview.ts (or under Markdown/) exposing:
    • KNOWN_DIRECTIVES: Set<string> mirroring VALID_ARGUMENTS keys.
    • formatDirectiveBlock(raw: string): { name, args, body } — reuses logic shape from client/src/components/Markdown/parse.ts:151 (FUNCTION_CALL_LINE_TEMPLATE) without depending on Vue/SectionWrapper.
  2. Extend useMarkdown in client/src/composables/markdown.ts with an opt-in flag galaxyDirectivePreview: boolean that registers a MarkdownIt rule replacing fence rendering for info === "galaxy" and a text rule (or post-render walk) for inline ${galaxy …}. Output static HTML with classes like chat-galaxy-directive, chat-galaxy-inline-directive. (Note: parse.ts:5 defines FUNCTION_CALL_LINE_TEMPLATE; parse.ts:151 is the usage site — both worth glancing at.)
  3. Set that flag at the useMarkdown call site: client/src/components/PageEditor/PageChatPanel.vue:47.
  4. Add scoped styles for the new classes in client/src/components/ChatGXY/ChatMessageCell.vue (pill header, collapse, inline chip) — these go in the existing <style scoped> deep-selectors block (:deep(pre) neighborhood at lines 201–213).
  5. Minor prompt nudge in lib/galaxy/agents/prompts/page_assistant.md: in the chat-vs-proposal section (~:31:33), explicitly say “when answering questions about directives, name the directive inline (history_dataset_display) rather than pasting a full fenced block unless illustrating syntax”. Keeps the existing strict embed-in-proposal guidance.
  6. Tests:
    • Extend client/src/components/ChatGXY/ChatMessageCell.test.ts with cases: known ```galaxy fence renders pill + body, unknown fence info-string renders as plain pre, inline ${galaxy history_dataset_name(...)} renders chip, malformed directive falls through to unchanged.
    • Add a client/src/composables/markdown.test.js case for the new galaxyDirectivePreview flag.
    • PageChatPanel.test.ts only needs a smoke assertion that the flag is on; no integration churn expected.
    • No backend tests — directive parser is unchanged.
  7. No new fixtures required; reuse strings already present in chat tests / parse.ts test inputs.

Open questions

(Implementation-time decisions deferred: collapse threshold, chip-arg detail, resolved-HID vs encoded-ID in chip header.)

References