) {
super.updated(changedProperties);
// Navigate when selectedNodeId changes or when patternSources arrive
if (
changedProperties.has("selectedNodeId") ||
changedProperties.has("patternSources")
) {
this.navigateToSelectedNode();
}
}
override render(): TemplateResult {
if (this.patternSources.length === 0) {
return html`
No pattern sources available. Click Refresh to load.
`;
}
// Clamp indices
const patternIdx = Math.min(
this.selectedPatternIdx,
this.patternSources.length - 1,
);
const pattern = this.patternSources[patternIdx];
if (!pattern || pattern.files.length === 0) {
return html`
Pattern has no source files.
`;
}
const fileIdx = Math.min(this.selectedFileIdx, pattern.files.length - 1);
const file = pattern.files[fileIdx];
const lines = file.contents.split("\n");
// Build annotations for current file
const annotations = this.buildAnnotations();
const fileAnnotations = annotations.get(file.name) ?? new Map();
// Use delta time for heat scaling when baseline exists, else total
const hasBaseline = this.baselineStats.size > 0;
let maxTime = 0;
for (const ann of fileAnnotations.values()) {
const t = hasBaseline ? ann.deltaTime : ann.totalTime;
if (t > maxTime) maxTime = t;
}
// Find which line the selected node is on
const selectedLoc = this.selectedNodeId
? parseActionLocation(this.selectedNodeId)
: null;
return html`
${lines.map((lineText, i) => {
const lineNum = i + 1;
const ann = fileAnnotations.get(lineNum);
const hasAction = !!ann;
const isSelected = selectedLoc?.file === file.name &&
selectedLoc?.line === lineNum;
const heatTime = ann
? (hasBaseline ? ann.deltaTime : ann.totalTime)
: 0;
const bgColor = ann
? this.heatColor(heatTime, maxTime, ann.types)
: "transparent";
const bpToggle = hasAction
? () => this.handleBreakpointToggle(ann!.entries)
: undefined;
const nodeSelect = hasAction
? () => this.handleLineClick(ann!.entries)
: undefined;
const bpClass = ann && this.hasAnyBreakpoint(ann.entries)
? "active"
: "";
// deno-fmt-ignore
return html` | ${lineNum} | ${ann ? ann.entries.map((entry: ActionEntry) => html``) : ""} | ${lineText} | ${ann ? this.renderLineStats(ann, hasBaseline) : ""} |
`;
})}
`;
}
private renderLineStats(
ann: LineAnnotation,
hasBaseline: boolean,
): TemplateResult {
if (hasBaseline) {
// Show delta time prominently
if (ann.deltaRuns === 0) {
return html`
idle
`;
}
return html`
+${this.formatTime(ann.deltaTime)}
(${ann.deltaRuns}x)${ann.entries.length > 1
? html`
[${ann.entries.length}]
`
: ""}
`;
}
// No baseline — show totals
return html`
${this.formatTime(ann.totalTime)} ${ann.runCount > 0
? `(${ann.runCount}x)`
: ""}${ann.entries.length > 1
? html`
[${ann.entries.length}]
`
: ""}
`;
}
}
customElements.define("x-scheduler-source", XSchedulerSource);