feat(ui): Integrate imgui_markdown and professional fonts for rich text rendering

This commit is contained in:
2026-03-08 23:07:42 -04:00
parent e802c6675f
commit 5f0168c4f2
10 changed files with 97 additions and 8 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,12 +1,12 @@
# Implementation Plan: Markdown Support & Syntax Highlighting # Implementation Plan: Markdown Support & Syntax Highlighting
## Phase 1: Markdown Integration & Setup ## Phase 1: Markdown Integration & Setup
- [ ] Task: Research and configure `imgui_markdown` within the existing `imgui-bundle` environment. - [x] Task: Research and configure `imgui_markdown` within the existing `imgui-bundle` environment.
- [ ] Identify required font assets for Markdown (bold, italic, headers). - [x] Identify required font assets for Markdown (bold, italic, headers).
- [ ] Create a `MarkdownRenderer` wrapper class in `src/markdown_helper.py` to manage styling and callbacks (links, etc.). - [x] Create a `MarkdownRenderer` wrapper class in `src/markdown_helper.py` to manage styling and callbacks (links, etc.).
- [ ] Task: Implement basic Markdown rendering in a test panel. - [x] Task: Implement basic Markdown rendering in a test panel.
- [ ] Verify that bold, italic, and headers render correctly using the defined theme fonts. - [x] Verify that bold, italic, and headers render correctly using the defined theme fonts.
- [ ] Task: Conductor - User Manual Verification 'Phase 1: Markdown Integration' (Protocol in workflow.md) - [x] Task: Conductor - User Manual Verification 'Phase 1: Markdown Integration' (Protocol in workflow.md)
## Phase 2: Syntax Highlighting Implementation ## Phase 2: Syntax Highlighting Implementation
- [ ] Task: Implement syntax highlighting for PowerShell, Python, and JSON/TOML. - [ ] Task: Implement syntax highlighting for PowerShell, Python, and JSON/TOML.

View File

@@ -799,6 +799,7 @@ class AppController:
"Tool Calls": False, "Tool Calls": False,
"Theme": True, "Theme": True,
"Log Management": False, "Log Management": False,
"Markdown Test": False,
} }
saved = self.config.get("gui", {}).get("show_windows", {}) saved = self.config.get("gui", {}).get("show_windows", {})
self.show_windows = {k: saved.get(k, v) for k, v in _default_windows.items()} self.show_windows = {k: saved.get(k, v) for k, v in _default_windows.items()}

View File

@@ -22,6 +22,7 @@ from src import log_pruner
from src import models from src import models
from src import app_controller from src import app_controller
from src import mcp_client from src import mcp_client
from src import markdown_helper
import re import re
from pydantic import BaseModel from pydantic import BaseModel
@@ -520,6 +521,13 @@ class App:
if self.show_windows.get("Diagnostics", False): if self.show_windows.get("Diagnostics", False):
self._render_diagnostics_panel() self._render_diagnostics_panel()
if self.show_windows.get("Markdown Test", False):
exp, opened = imgui.begin("Markdown Test", self.show_windows["Markdown Test"])
self.show_windows["Markdown Test"] = bool(opened)
if exp:
self._render_markdown_test()
imgui.end()
self.perf_monitor.end_frame() self.perf_monitor.end_frame()
# ---- Modals / Popups # ---- Modals / Popups
with self._pending_dialog_lock: with self._pending_dialog_lock:
@@ -1212,6 +1220,29 @@ class App:
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_diagnostics_panel") if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_diagnostics_panel")
imgui.end() imgui.end()
def _render_markdown_test(self) -> None:
imgui.text("Markdown Test Panel")
imgui.separator()
md = """
# Header 1
## Header 2
### Header 3
This is **bold** text and *italic* text.
And ***bold italic*** text.
* List item 1
* List item 2
* Sub-item
[Link to Google](https://google.com)
```python
def hello():
print("Markdown works!")
```
"""
markdown_helper.render(md)
def _render_files_panel(self) -> None: def _render_files_panel(self) -> None:
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_files_panel") if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_files_panel")
@@ -2154,7 +2185,7 @@ class App:
self.bulk_block() self.bulk_block()
# Table # Table
flags = imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable | imgui.TableFlags_.scroll_y flags = imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable | imgui.TableFlags_.scroll_y
if imgui.begin_table("ticket_queue_table", 6, flags, imgui.ImVec2(0, 300)): if imgui.begin_table("ticket_queue_table", 7, flags, imgui.ImVec2(0, 300)):
imgui.table_setup_column("Select", imgui.TableColumnFlags_.width_fixed, 40) imgui.table_setup_column("Select", imgui.TableColumnFlags_.width_fixed, 40)
imgui.table_setup_column("ID", imgui.TableColumnFlags_.width_fixed, 80) imgui.table_setup_column("ID", imgui.TableColumnFlags_.width_fixed, 80)
imgui.table_setup_column("Priority", imgui.TableColumnFlags_.width_fixed, 100) imgui.table_setup_column("Priority", imgui.TableColumnFlags_.width_fixed, 100)
@@ -2858,7 +2889,8 @@ class App:
self.runner_params.callbacks.load_additional_fonts = self._load_fonts self.runner_params.callbacks.load_additional_fonts = self._load_fonts
self.runner_params.callbacks.post_init = self._post_init self.runner_params.callbacks.post_init = self._post_init
self._fetch_models(self.current_provider) self._fetch_models(self.current_provider)
immapp.run(self.runner_params) md_options = markdown_helper.get_renderer().options
immapp.run(self.runner_params, add_ons_params=immapp.AddOnsParams(with_markdown_options=md_options))
# On exit # On exit
self.shutdown() self.shutdown()
session_logger.close_session() session_logger.close_session()

56
src/markdown_helper.py Normal file
View File

@@ -0,0 +1,56 @@
# src/markdown_helper.py
from __future__ import annotations
from imgui_bundle import imgui_md, imgui, immapp
import webbrowser
import os
from typing import Optional
class MarkdownRenderer:
"""
Wrapper for imgui_md to manage styling, callbacks, and specialized rendering
(like syntax highlighting integration).
"""
def __init__(self):
self.options = imgui_md.MarkdownOptions()
# Use Inter as the base font for Markdown (matches professional theme)
# It expects fonts like Inter-Regular.ttf, Inter-Bold.ttf, etc. in the assets folder
self.options.font_options.font_base_path = "fonts/Inter"
self.options.font_options.regular_size = 16.0
# Configure callbacks
self.options.callbacks.on_open_link = self._on_open_link
# Note: Syntax highlighting will be integrated in Phase 2
def _on_open_link(self, url: str) -> None:
"""Handle link clicks in Markdown."""
# If it's a URL, open in browser
if url.startswith("http"):
webbrowser.open(url)
else:
# Handle local files or internal links
# For now, just print. Could integrate with app_controller in Phase 4.
print(f"Clicked local link: {url}")
def render(self, text: str) -> None:
"""Render Markdown text using imgui_md."""
imgui_md.render(text)
def render_unindented(self, text: str) -> None:
"""Render Markdown text with automatic unindentation."""
imgui_md.render_unindented(text)
# Global instance for easy access
_renderer: Optional[MarkdownRenderer] = None
def get_renderer() -> MarkdownRenderer:
global _renderer
if _renderer is None:
_renderer = MarkdownRenderer()
return _renderer
def render(text: str) -> None:
get_renderer().render(text)
def render_unindented(text: str) -> None:
get_renderer().render_unindented(text)