@escouade/graph-trace-viewer
v0.1.2
Published
Vue 3 component to render a graph-trace document — chat-style messages, tool calls, token usage and a graph view.
Maintainers
Readme
@escouade/graph-trace-viewer
Vue 3 component to render a trace document produced by
@escouade/graph-trace — a zero-infra,
post-mortem viewer for graph agent runs (chat-style messages, tool calls, token usage, and a graph view
of the executed workflow).
- LLM vocabulary — renders messages, tool calls, token usage and the run timeline, not opaque span blobs.
- Graph + execution views — overview of the workflow topology and a linear, expandable step sequence over the same trace.
- Open a file, no server — the host wires a
TraceSourceport (load / open / export); the viewer renders whatever it returns. - Diagram engine injected — markdown diagram blocks (e.g. mermaid) render through a host-provided
DiagramRenderer, so the viewer ships no heavy diagram dependency.
Install
npm install @escouade/graph-trace-viewer vue primevuevue (3.5+) and primevue (4+) are peer dependencies.
@escouade/graph-trace is an optional
peer (handy for parseTraceDocument and the shared types).
Theming
Every component reads from a fixed set of --ds-* CSS custom properties (--ds-bg, --ds-text,
--ds-accent, …). By default these derive from your app's PrimeVue preset — they alias the
PrimeVue semantic tokens (--p-content-background, --p-text-color, --p-primary-color, …), so the
viewer automatically adopts your theme (Aura, Lara, a custom definePreset, …) and follows its
darkModeSelector for light/dark. No palette to copy.
The header ships a light/dark toggle (button next to "Télécharger la trace") that flips the PrimeVue
dark-mode class on <html>. Tell it which class via the darkModeClass prop — it must match your
darkModeSelector (e.g. darkModeSelector: 'html.dark' → darkModeClass="dark", the default). On
first load the mode is resolved as: previous localStorage choice → OS preference
(prefers-color-scheme) → light. If your app owns its own theme switching, pass
:showThemeToggle="false" to hide the button.
To override the palette directly, define your own --ds-* values on :root in your app's CSS — not
wrapped in an @layer block:
:root {
--ds-bg: #0d1117;
--ds-text: #c9d1d9;
--ds-accent: #58a6ff;
/* … see src/themes/tokens.css for the full token list */
}This works regardless of where your stylesheet is imported relative to the viewer's: the default tokens
are declared inside a CSS layer (@layer ds-theme), and per the CSS Cascade Layers spec, any unlayered
rule always wins over a layered one. So your override stays stable even if a future version of this
package changes its defaults.
Usage
The viewer reads through a TraceSource port that the host implements (wired to its own storage —
file picker, fetch, IPC…):
<script setup lang="ts">
import { TraceViewer, type TraceSource } from '@escouade/graph-trace-viewer';
import { parseTraceDocument } from '@escouade/graph-trace';
const source: TraceSource = {
// Load a conversation's trace by id (e.g. fetch from your backend).
async load(traceId) {
const res = await fetch(`/traces/${traceId}.json`);
return parseTraceDocument(await res.text());
},
// Reopen an exported file via a picker; return null if cancelled.
async open() {
return null;
},
// Persist/export the current trace; return null if cancelled.
async export() {
return null;
},
};
</script>
<template>
<TraceViewer :source="source" trace-id="conv-1" />
</template>Rendering diagrams (mermaid)
The viewer carries no diagram engine. To render fenced ```mermaid blocks, pass a DiagramRenderer.
A mermaid-backed one ships as a helper — you supply the mermaid instance, the viewer never imports it:
<script setup lang="ts">
import { TraceViewer, createMermaidDiagramRenderer } from '@escouade/graph-trace-viewer';
import mermaid from 'mermaid';
const diagramRenderer = createMermaidDiagramRenderer(mermaid);
</script>
<template>
<TraceViewer :source="source" trace-id="conv-1" :diagram-renderer="diagramRenderer" />
</template>Without a diagramRenderer, diagram blocks degrade gracefully to their text. Any engine works: implement
the DiagramRenderer port (render(blocks: HTMLElement[]): Promise<void>) yourself.
Architecture
Source layout
src/
components/ # trace-domain Vue components (TraceViewer, TraceTimeline, …)
composables/ # shared reactive state (panel-state, theme)
trace/ # domain logic and types (trace.types, trace-utils, derive-iterations)
ports/ # DIP ports: DiagramRenderer injection key and factory
ui/ # generic presentational primitives (no trace-domain knowledge)
themes/ # CSS custom properties — tokens.css (aliases PrimeVue semantics)Inversion of control
The viewer ships two ports that the host must or may wire up:
| Port | Direction | Purpose |
|---|---|---|
| TraceSource | prop on <TraceViewer> | load / open / export trace documents |
| DiagramRenderer | prop on <TraceViewer> | render diagram blocks (e.g. mermaid) |
Neither is coupled to a specific backend or diagram engine. The viewer stays dependency-free from both.
ui/ boundary
Components in ui/ have no trace-domain imports — they do not know about TraceTurn,
WorkflowNode, or any composable from composables/. They could be lifted into any Vue project
unchanged. Everything that references the trace domain lives in components/.
License
MIT
