Skip to content
This repository was archived by the owner on Sep 10, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 60 additions & 27 deletions mixtape/core/static/mixtape/core/css/theme.css
Original file line number Diff line number Diff line change
@@ -1,33 +1,66 @@
:root {
/* Mapped from figma_changes_data/colors.js (light) */
--color-surface: #FFFFFF; /* background */
--color-surface-foreground: #141516; /* text-primary */
--color-border: #C9CBCE; /* border */
--color-muted: rgba(0, 0, 0, 0.03); /* surface */
--color-muted-foreground: rgba(20, 21, 22, 0.6); /* text-secondary */
--color-accent: #0000FF; /* primary */
--color-accent-foreground: #FFFFFF; /* text-button */
}
.dark {
/* Mapped from figma_changes_data/colors.js (dark) */
--color-surface: #111827; /* background */
--color-surface-foreground: #FFFFFF; /* text-primary */
--color-border: #4C4C4C; /* border-light */
--color-muted: rgba(255, 255, 255, 0.05); /* surface */
--color-muted-foreground: rgba(255, 255, 255, 0.7); /* text-secondary */
--color-accent: #ACD5FF; /* primary */
--color-accent-foreground: #141516; /* text-button */
color-scheme: "light";
--color-base-100: oklch(100% 0 0);
--color-base-200: oklch(98% 0 0);
--color-base-300: oklch(95% 0 0);
--color-base-content: oklch(21% 0.006 285.885);
--color-primary: oklch(45% 0.24 277.023);
--color-primary-content: oklch(93% 0.034 272.788);
--color-secondary: oklch(65% 0.241 354.308);
--color-secondary-content: oklch(94% 0.028 342.258);
--color-accent: oklch(77% 0.152 181.912);
--color-accent-content: oklch(38% 0.063 188.416);
--color-neutral: oklch(14% 0.005 285.823);
--color-neutral-content: oklch(92% 0.004 286.32);
--color-info: oklch(74% 0.16 232.661);
--color-info-content: oklch(29% 0.066 243.157);
--color-success: oklch(76% 0.177 163.223);
--color-success-content: oklch(37% 0.077 168.94);
--color-warning: oklch(82% 0.189 84.429);
--color-warning-content: oklch(41% 0.112 45.904);
--color-error: oklch(71% 0.194 13.428);
--color-error-content: oklch(27% 0.105 12.094);
--radius-selector: 0.5rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
--size-selector: 0.25rem;
--size-field: 0.25rem;
--border: 1px;
--depth: 1;
--noise: 0;

}

@media (prefers-color-scheme: dark) {
:root {
/* Mirror .dark variables when user prefers dark mode */
--color-surface: #111827; /* background */
--color-surface-foreground: #FFFFFF; /* text-primary */
--color-border: #4C4C4C; /* border-light */
--color-muted: rgba(255, 255, 255, 0.05); /* surface */
--color-muted-foreground: rgba(255, 255, 255, 0.7); /* text-secondary */
--color-accent: #ACD5FF; /* primary */
--color-accent-foreground: #141516; /* text-button */
color-scheme: "dark";
--color-base-100: #111827;
--color-base-200: #1e2939; /* --color-gray-800 */
--color-base-300: #4c4c4c;
--color-base-content: oklch(97.807% 0.029 256.847);
--color-primary: oklch(58% 0.233 277.117);
--color-primary-content: oklch(96% 0.018 272.314);
--color-secondary: oklch(65% 0.241 354.308);
--color-secondary-content: oklch(94% 0.028 342.258);
--color-accent: oklch(77% 0.152 181.912);
--color-accent-content: oklch(38% 0.063 188.416);
--color-neutral: oklch(14% 0.005 285.823);
--color-neutral-content: oklch(92% 0.004 286.32);
--color-info: oklch(74% 0.16 232.661);
--color-info-content: oklch(29% 0.066 243.157);
--color-success: oklch(76% 0.177 163.223);
--color-success-content: oklch(37% 0.077 168.94);
--color-warning: oklch(82% 0.189 84.429);
--color-warning-content: oklch(41% 0.112 45.904);
--color-error: oklch(71% 0.194 13.428);
--color-error-content: oklch(27% 0.105 12.094);
--radius-selector: 0.5rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
--size-selector: 0.25rem;
--size-field: 0.25rem;
--border: 1px;
--depth: 1;
--noise: 0;
}
}
/* Deprecated helper classes removed (migrated to Tailwind utilities). */
135 changes: 14 additions & 121 deletions mixtape/core/static/mixtape/core/insights/insightsTabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,11 @@ document.addEventListener('alpine:init', () => {
maxSteps: 0,
maxStepsGlobal: 0,
episodeSummaries: [],
selectedEpisodeIdx: 0,
timelineEpisodeIdx: 0,
mediaEpisodeIdx: 0,
linkMediaToEpisode: true,
mediaFitMode: 'contain',
limitToEpisode: false,
debug: false,
playing: false,
useGlobalTimeline: true,
playbackSpeed: 1,
visOptions: [
'actionRewardFrequency',
'rewardFrequency',
'actionFrequency',
'rewardsOverTime',
'obsClustering',
'actionClustering',
'plotLegends',
'showPlayback',
'showEpisodeInfo',
'showMediaViewer',
'showTimeline',
],
resizeQueued: false,
multiKey: '',
clusteringAvailable: false,
pollTimer: null,
Expand Down Expand Up @@ -109,9 +90,9 @@ document.addEventListener('alpine:init', () => {
// Build watchers
this.updateMaxSteps();
this.$watch('limitToEpisode', () => this.updateMaxSteps());
this.$watch('selectedEpisodeIdx', () => {
this.$watch('$store.settings.timelineEpisodeIdx', () => {
this.updateMaxSteps();
if (this.linkMediaToEpisode) this.mediaEpisodeIdx = this.selectedEpisodeIdx;
if (this.linkMediaToEpisode) this.mediaEpisodeIdx = this.$store.settings.timelineEpisodeIdx;
});
this.$watch('episodeSummaries', () => {
if (!Array.isArray(this.episodeSummaries) || this.episodeSummaries.length === 0) {
Expand All @@ -120,50 +101,27 @@ document.addEventListener('alpine:init', () => {
this.mediaEpisodeIdx = 0;
}
});
this.$watch('linkMediaToEpisode', (v) => { if (v) { this.mediaEpisodeIdx = this.selectedEpisodeIdx; } });
this.$watch('linkMediaToEpisode', (v) => { if (v) { this.mediaEpisodeIdx = this.$store.settings.timelineEpisodeIdx; } });
// Ensure timeline mode changes recompute limits
this.$watch('useGlobalTimeline', () => { this.updateMaxSteps(); });
this.$watch('$store.settings.useGlobalTimeline', this.updateMaxSteps);

// Store
const store = (window.Alpine && Alpine.store('insights')) || null;
if (store) {
// Pull settings
if (typeof store.playbackSpeed === 'number') this.playbackSpeed = store.playbackSpeed;
if (typeof store.useGlobalTimeline === 'boolean') this.useGlobalTimeline = store.useGlobalTimeline;
if (typeof store.timelineEpisodeIdx === 'number') this.timelineEpisodeIdx = store.timelineEpisodeIdx;
if (Array.isArray(store.visOptions)) this.visOptions = store.visOptions;
// Push derived values
store.maxStepsGlobal = this.maxStepsGlobal;
store.currentStep = this.currentStep;
store.episodeSummaries = this.episodeSummaries;
store.multiKey = this.multiKey;
store.clusteringAvailable = this.clusteringAvailable;
store.clustering = this.clustering;
}
// push changes
this.$watch('useGlobalTimeline', v => { Alpine.store('insights').useGlobalTimeline = v; });
this.$watch('timelineEpisodeIdx', v => { Alpine.store('insights').timelineEpisodeIdx = v; });
this.$watch('episodeSummaries', v => { Alpine.store('insights').episodeSummaries = v; });
this.$watch('playbackSpeed', v => { Alpine.store('insights').playbackSpeed = v; });
this.$watch('visOptions', v => { Alpine.store('insights').visOptions = v; });
this.$watch('maxStepsGlobal', v => { Alpine.store('insights').maxStepsGlobal = v; });
this.$watch('currentStep', v => { Alpine.store('insights').currentStep = v; });
this.$watch('clustering', (v) => {
const store = (window.Alpine && Alpine.store('insights')) || null;
if (store) store.clustering = v;
});
// pull changes
this.$watch('$store.insights.useGlobalTimeline', v => { this.useGlobalTimeline = v; this.updateMaxSteps(); });
this.$watch('$store.insights.timelineEpisodeIdx', v => { this.timelineEpisodeIdx = v; });
this.$watch('$store.insights.playbackSpeed', v => { this.playbackSpeed = v; });
this.$watch('$store.insights.visOptions', v => {
this.visOptions = Array.isArray(v) ? v : this.visOptions;
this.queuePlotResize();
});
// side panel collapse/expand can change available width
this.$watch('$store.insights.sidePanelCollapsed', () => { this.queuePlotResize(); });
// Observe once on init for plots that become visible later
this.$nextTick(() => { this.observeResizeOnVisible(); });

// Polling: multi-episode artifact or single-episode partials
const isMulti = Array.isArray(this.episodeIds) && this.episodeIds.length > 1;
Expand All @@ -177,50 +135,28 @@ document.addEventListener('alpine:init', () => {
stepForward() {
if (!this.playing) return;
if (this.currentStep < this.maxSteps) {
const delay = Math.max(50, Math.round(500 / (this.playbackSpeed || 1)));
const delay = Math.max(50, Math.round(500 / (this.$store.settings.playbackSpeed || 1)));
setTimeout(() => { this.currentStep++; this.stepForward(); }, delay);
} else { this.playing = false; this.currentStep = 0; }
},
updateMaxSteps() {
if (this.limitToEpisode) {
const steps = this.episodeSummaries[this.selectedEpisodeIdx]?.steps ?? null;
const steps = this.episodeSummaries[this.$store.settings.timelineEpisodeIdx]?.steps ?? null;
this.maxSteps = (typeof steps === 'number' && steps > 0) ? steps - 1 : this.maxStepsGlobal;
} else { this.maxSteps = this.maxStepsGlobal; }
},

tabClass(tab) {
return this.active === tab ? 'tab-active' : 'tab-inactive';
},
selectTab(tab) {
this.active = tab;
this.queuePlotResize();
},
overviewVisible() {
const o = Array.isArray(this.visOptions) ? this.visOptions : [];
const c = (o.includes('rewardFrequency') ? 1 : 0)
+ (o.includes('actionFrequency') ? 1 : 0)
+ (o.includes('actionRewardFrequency') ? 1 : 0);
return c > 0;
},
overviewGridColsClass() {
const o = Array.isArray(this.visOptions) ? this.visOptions : [];
const c = (o.includes('rewardFrequency') ? 1 : 0)
+ (o.includes('actionFrequency') ? 1 : 0)
+ (o.includes('actionRewardFrequency') ? 1 : 0);
return c >= 3 ? 'lg:grid-cols-3' : (c === 2 ? 'lg:grid-cols-2' : 'lg:grid-cols-1');
},
maxStepsTooltip() {
const numEpSummaries = Array.isArray(this.episodeSummaries) ? this.episodeSummaries.length : 0;
if (numEpSummaries <= 1) {
const steps = this.episodeSummaries?.[0]?.steps ?? null;
return `Total Steps: ${steps ?? '—'}`;
if (this.episodeSummaries.length <= 1) {
return `Total Steps: ${this.episodeSummaries[0].steps ?? '—'}`;
}
const lines = [`Max Steps: ${this.maxStepsCount ?? '—'}`];
for (let i = 0; i < numEpSummaries; i++) {
const steps = this.episodeSummaries?.[i]?.steps ?? '—';
lines.push(`Ep ${i + 1}: ${steps}`);
}
return lines.join('\n');
return [
`Max Steps: ${this.maxStepsCount ?? '—'}`,
...this.episodeSummaries
.map((episodeSummary) => `Ep ${episodeSummary.id}: ${episodeSummary.steps ?? '-'}`)
].join('\n');
},
timelineTitle(ts) {
if (Array.isArray(ts?.episodes)) {
Expand Down Expand Up @@ -255,7 +191,7 @@ document.addEventListener('alpine:init', () => {
},
// Compatibility alias used by some templates
getStep(step) {
return this.getEpisodeStepData(this.selectedEpisodeIdx);
return this.getEpisodeStepData(this.$store.settings.timelineEpisodeIdx);
},
getAgentStep(epIdx, agent) {
const epStepData = this.getEpisodeStepData(epIdx);
Expand Down Expand Up @@ -283,48 +219,6 @@ document.addEventListener('alpine:init', () => {
}
return pairs;
},
resizePlots(panelId) {
if (!window.Plotly) return;
const root = this.$el;
const scope = panelId ? root.querySelector(`#${panelId}`) : root;
if (!scope) return;
const nodes = scope.querySelectorAll('.js-plotly-plot, [data-plotly]');
nodes.forEach((el) => {
Plotly.Plots.resize(el);
Plotly.relayout(el, { autosize: true });
});
},
queuePlotResize() {
// Debounce resize to prevent Plotly "jumpiness"
if (this.resizeQueued) return;
this.resizeQueued = true;
this.$nextTick(() => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
this.resizePlots();
this.observeResizeOnVisible();
this.resizeQueued = false;
});
});
});
},
observeResizeOnVisible(panelId) {
// Make sure resize runs when plots become visible
if (!window.Plotly || !window.IntersectionObserver) return;
const root = this.$el;
const scope = panelId ? root.querySelector(`#${panelId}`) : root;
if (!scope) return;
const nodes = scope.querySelectorAll('.js-plotly-plot, [data-plotly]');
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
Plotly.Plots.resize(e.target);
io.unobserve(e.target);
}
});
}, { root: null, threshold: 0 });
nodes.forEach((el) => { io.observe(el); });
},
totalFromSeries(yTrace) {
const rewards = Array.isArray(yTrace) ? yTrace.filter(reward => typeof reward === 'number') : [];
if (!rewards.length) return null;
Expand Down Expand Up @@ -457,7 +351,6 @@ document.addEventListener('alpine:init', () => {
this.clustering = obj;
const store = (window.Alpine && Alpine.store('insights')) || null;
if (store) store.clustering = obj;
this.queuePlotResize();
if (this.clustering && ((this.clustering.obs && this.clustering.agent_outs) || isMulti)) {
this.toastText = 'Clustering computation completed';
this.showToast = true;
Expand Down
32 changes: 18 additions & 14 deletions mixtape/core/static/mixtape/core/insights/rewardsFrequency.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,27 @@ document.addEventListener('alpine:init', () => {
title: {text: 'Rewards VS Frequency'},
xaxis: {
title: {text: 'Reward'},
gridcolor: Alpine.store('theme').axis.gridcolor,
zerolinecolor: Alpine.store('theme').axis.zerolinecolor
gridcolor: Alpine.store('theme').gridColor,
linecolor: Alpine.store('theme').gridColor,
zeroline: false,
},
yaxis: {
title: {text: 'Frequency'},
gridcolor: Alpine.store('theme').axis.gridcolor,
zerolinecolor: Alpine.store('theme').axis.zerolinecolor
gridcolor: Alpine.store('theme').gridColor,
linecolor: Alpine.store('theme').gridColor,
zeroline: false,
},
autosize: true,
barmode: 'overlay',
showlegend: Alpine.store('insights').visOptions.includes('plotLegends'),
paper_bgcolor: Alpine.store('theme').paper_bgcolor,
plot_bgcolor: Alpine.store('theme').plot_bgcolor,
font: Alpine.store('theme').font,
showlegend: Alpine.store('settings').showPlotLegends,
paper_bgcolor: Alpine.store('theme').backgroundColor,
plot_bgcolor: Alpine.store('theme').backgroundColor,
font: {
color: Alpine.store('theme').fontColor
},
},
config: {
displayModeBar: false,
responsive: true
},
plot: null,
init() {
Expand All @@ -39,19 +42,20 @@ document.addEventListener('alpine:init', () => {
});
});
this.plot = Plotly.newPlot(this.$refs.rewardsFrequency, this.data, this.layout, this.config);
this.$nextTick(() => { Plotly.Plots.resize(this.$refs.rewardsFrequency); });
window.addEventListener('resize', () => { Plotly.Plots.resize(this.$refs.rewardsFrequency); });
this.$watch('$store.insights.visOptions', () => {
this.$watch('$store.settings.showPlotLegends', () => {
this.$nextTick(() => {
Plotly.relayout(
this.$refs.rewardsFrequency,
{
autosize: true,
showlegend: this.$store.insights.visOptions.includes('plotLegends')
showlegend: this.$store.settings.showPlotLegends
})
});
});

},
resizePlot: _.debounce(function() {
if (!this.$refs.rewardsFrequency.querySelector('.plotly')) return;
Plotly.Plots.resize(this.$refs.rewardsFrequency);
}, 200, {leading: true}),
}));
});
Loading