MCPcopy Create free account
hub / github.com/ScriptedAlchemy/tracedecay / TimelineChart

Function TimelineChart

dashboard/lcm/src/components.tsx:149–197  ·  view source on GitHub ↗
(props: { buckets?: any[]; nodeBuckets?: any[]; undatedCount?: any })

Source from the content-addressed store, hash-verified

147/** Responsive CSS bar chart (no SVG stretching, so bars stay crisp and the
148 * summary markers render as true round dots regardless of bucket count). */
149export function TimelineChart(props: { buckets?: any[]; nodeBuckets?: any[]; undatedCount?: any }): React.ReactElement {
150 // Buckets cover only messages with real timestamps; the server reports
151 // messages without one separately so they surface as an honest note
152 // instead of a fake single bar.
153 const buckets = (props.buckets || []).filter(function (b) { return b && b.bucket != null; });
154 const nodeBuckets = props.nodeBuckets || [];
155 const undatedCount = Number(props.undatedCount) || 0;
156 if (!buckets.length) {
157 return (
158 <EmptyState className="hermes-lcm-empty">
159 {undatedCount > 0
160 ? `No dated messages yet — ${fmtInt(undatedCount)} stored messages have no timestamp`
161 : "No timeline data"}
162 </EmptyState>
163 );
164 }
165 const maxCount = buckets.reduce((acc, b) => Math.max(acc, Number(b.count) || 0), 0) || 1;
166 const nodeByBucket: Record<string, number> = {};
167 nodeBuckets.forEach(function (nb) { nodeByBucket[nb.bucket] = Number(nb.count) || 0; });
168
169 const cols = buckets.map(function (b, i) {
170 const count = Number(b.count) || 0;
171 const pct = count > 0 ? Math.max(3, Math.round((count / maxCount) * 100)) : 0;
172 const nodes = nodeByBucket[b.bucket] || 0;
173 const tip = `${b.bucket}: ${fmtInt(count)} messages`
174 + (nodes ? ` · ${fmtInt(nodes)} summaries` : "");
175 return (
176 <div key={b.bucket + i} className="hermes-lcm-tl-col" title={tip}>
177 <div className={"hermes-lcm-tl-dot" + (nodes ? " hermes-lcm-tl-dot-on" : "")} />
178 <div className="hermes-lcm-tl-bar" style={{ height: pct + "%" }} />
179 </div>
180 );
181 });
182
183 return (
184 <div className="hermes-lcm-tl">
185 <div className="hermes-lcm-tl-bars">{cols}</div>
186 <div className="hermes-lcm-svg-axis">
187 <span>{short(buckets[0].bucket, 16)}</span>
188 <span>{short(buckets[buckets.length - 1].bucket, 16)}</span>
189 </div>
190 {undatedCount > 0 ? (
191 <div className="hermes-lcm-dim hermes-lcm-tl-undated">
192 {`${fmtInt(undatedCount)} undated messages not shown`}
193 </div>
194 ) : null}
195 </div>
196 );
197}
198
199/** Per-group compression (kept vs saved), rendered as a small inline SVG. */
200export function CompressionBars(props: { groups?: any[]; onPick?: (g: any) => void }): React.ReactElement {

Callers

nothing calls this directly

Calls 2

fmtIntFunction · 0.90
shortFunction · 0.90

Tested by

no test coverage detected