Repository navigation
Replace Avalonia entity browser view with HTML-based renderer using ControllableWebViewControl #41
Description
Summary
The entity browser (EntityBrowserWorkspaceTabView) currently renders entities as a tree of native Avalonia EntityCardControl instances inside a ScrollViewer + ItemsControl. The proposal is to replace the entire rendering layer with an HTML surface driven by ControllableWebViewControl / IControllableBrowser, reusing the JS-bridge plumbing already established for the chat output control.
The view is read-only. No Avalonia entity card content is retained. Editing is explicitly out of scope.
Architecture
Replace the ScrollViewer + ItemsControl + EntityCardControl stack with a single ControllableWebViewControl, driven by a new EntityBrowserHtmlModel analogous to ChatOutputHtmlModel:
EntityBrowserWorkspaceTabView
└── ControllableWebViewControl (IControllableBrowser)
↕ JS bridge (JSON commands)
EntityBrowserHtmlModel
├── EntityCardHtmlRenderer (pure HTML generation, stateless)
└── EntityCardHtmlTransformer (collection-change → DOM ops)
FieldEditorFactory and all EntityFieldEditorViewModel subclasses are retained. They already encode all field-type identification and rendering decisions. EntityCardHtmlRenderer reads from the view model tree and emits HTML — it does not re-implement field-type logic.
Rendering decisions
Card shell
Each entity card renders:
- Header row:
DisplayName(title style) +EntityType(muted label) - Actions row (top-right): interest badges, status badges, shortcut buttons (in that order)
- Field list: one row per field editor (see per-type rules below)
- Cards separated by border radius and card-surface background, matching the existing
entity-cardstyle
Interest badges (BadgeModel)
Each BadgeModel has: Glyph, Tooltip, IsActive (bool).
- Render as small clickable buttons in the header actions row.
- Active badges are visually highlighted (e.g. filled/accent background).
- Clicking sends
{ type: "toggleInterest", interestTypeName: "...", entityId: "..." }to the host.
Status badges (StatusBadgeModel)
Each StatusBadgeModel has: StatusValue (text), BrushKey (color key), Tooltip.
- Render as colored pills (background color from
BrushKey, resolved to a CSS variable at render time). - Display-only — not interactive.
Tooltipshows the field name (disambiguates multiple status fields).
Shortcuts (EntityShortcutViewModel / Shortcut)
Known shortcuts: Open (↗), Json ({}), Delete (🗑), StartAgentSession (🤖), StartShell (💻).
Each has Label (the glyph/text displayed), HoverText (tooltip), and IsEnabled.
- Render as small buttons in the header actions row after status badges.
- Disabled shortcuts are greyed /
pointer-events: none. - Clicking sends
{ type: "shortcut", shortcutName: "...", entityId: "..." }to the host.
Field rendering rules by view model type
StringFieldEditorViewModel
- Render
Valueas plain text.
BooleanToggleFieldEditorViewModel
- Render
DisplayValue("true" or "false") as plain text.
LocalStringFieldEditorViewModel
- Render the active locale's
Valueas plain text. - If
IsLocalized, append the active locale tag in muted style.
MimeAttachmentFieldEditorViewModel
- Inline mode (
IsInline == true): render content body only, no field label, no chrome. Used for note content fields.text/markdown→ renderTextContentas Markdown HTML (via Markdig safe pipeline, per Render assistant text output as Markdown into HTML elements #37).- Other mime types → render
TextContentin a<pre>block.
- Non-inline mode (
ShowChrome == true): render expandable block with mime-type, optional url, and content.text/markdown→ Markdown HTML inside the expander.- Other mime types →
<pre>block inside the expander.
LocalizedMimeAttachmentFieldEditorViewModel
- Delegate to
ActiveEditorfollowing the inline/chrome rules above. - If
IsLocalized, append active locale tag in muted style.
JsonSchemaFieldEditorViewModel
- Render
MarkdownText(pre-formatted as a```json ```fenced block) as Markdown HTML.
EntityReferenceFieldEditorViewModel
- Render
ResolvedDisplayNameas a clickable link. - Clicking sends
{ type: "openEntity", entityId: "..." }to host. - Tooltip:
TooltipText(entity names + id).
EntityListFieldEditorViewModel
- Render each
EntityReferenceListItemViewModelas a clickable link (sameopenEntitymessage). - Empty list renders as muted "(none)" or is omitted.
ObjectFieldEditorViewModel
- Render
Fieldsrecursively as a nested field list (indented).
ArrayFieldEditorViewModel
- Render
Itemsrecursively as a bulleted list.
Field label rule
FieldNameshown as a muted prefix for simple scalar fields.- Inline mime attachments suppress the label entirely.
- Object/array fields show the label above the nested content.
Markdown rendering
text/markdown mime fields and JsonSchemaFieldEditorViewModel.MarkdownText are rendered via Markdig with a safe pipeline (no raw HTML), consistent with #37.
Theme propagation
Map Avalonia resource keys → CSS custom properties, send on Ready, re-send on OnActualThemeVariantChanged (not repeating the #38 mistake). CSS variables cover: background, foreground, card-surface, muted, accent, link, status badge colours (good/bad/other).
Incremental updates
EntityBrowserHtmlModel subscribes to EntityListViewModel.Items collection changes. EntityCardHtmlTransformer diffs and emits update/remove/append commands — same CollectionTransformer pattern as ChatMessageHtmlTransformer. Individual field updates diff at the field-editor level using a content key.
Open design questions
- Bridge command protocol. Extend
ChatOutputBrowserCommandsinto a sharedBrowserCommands(preferred) or define a separateEntityBrowserCommands? - HTML shell. Separate
entity-browser-shell.htmlembedded resource, or a shared shell parameterised at load time? - Status badge colors.
BrushKeyresolves to an Avalonia brush in the current code. The HTML renderer needs the resolved CSS hex at render time, or a CSS variable per status class (good/bad/other) set via theThemecommand.
Affected files (expected)
Phantom.Workspaces/Templates/EntityBrowserWorkspaceTabView.axaml— replace withControllableWebViewControlPhantom.Workspaces/Templates/EntityBrowserWorkspaceTabView.axaml.cs— wiringEntityBrowserHtmlModelPhantom.Workspaces/ViewModels/EntityBrowserHtmlModel.cs— newPhantom.Workspaces/ViewModels/EntityCardHtmlRenderer.cs— newPhantom.Workspaces/Assets/entity-browser-shell.html— new- Possibly shared
BrowserCommandsreplacingChatOutputBrowserCommands
Related
- Investigate browser-hosted renderer for slow SelectableTextBox output #32 — built
ControllableWebViewControl/IControllableBrowser - Render assistant text output as Markdown into HTML elements #37 — Markdown rendering via Markdig in HTML surfaces
- Dark/light theme change not propagated to HTML-based chat output control #38 — theme propagation bug (avoid repeating here)
- WebView reload/refresh wipes all chat content (DOM never repopulated) #39 — WebView reload bug (fix before or alongside this)
Activity