|
|
--- |
|
|
// TrackioWrapper.astro |
|
|
import Trackio from "./Trackio.svelte"; |
|
|
--- |
|
|
|
|
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" /> |
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> |
|
|
<link |
|
|
href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;600;700&display=swap" |
|
|
rel="stylesheet" |
|
|
/> |
|
|
|
|
|
<div class="trackio-wrapper"> |
|
|
<div class="trackio-controls"> |
|
|
<div class="controls-left"> |
|
|
<div class="theme-selector"> |
|
|
<label for="theme-select">Theme</label> |
|
|
<select id="theme-select" class="theme-select"> |
|
|
<option value="classic">Classic</option> |
|
|
<option value="oblivion">Oblivion</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="scale-controls"> |
|
|
<label> |
|
|
<input type="checkbox" id="log-scale-x" checked /> |
|
|
Log Scale X |
|
|
</label> |
|
|
<label> |
|
|
<input type="checkbox" id="smooth-data" checked /> |
|
|
Smooth |
|
|
</label> |
|
|
</div> |
|
|
</div> |
|
|
<div class="controls-right"> |
|
|
<button class="button button--ghost" type="button" id="randomize-btn"> |
|
|
Randomize Data |
|
|
</button> |
|
|
<button |
|
|
class="button button--primary" |
|
|
type="button" |
|
|
id="start-simulation-btn" |
|
|
> |
|
|
Live Run |
|
|
</button> |
|
|
<button |
|
|
class="button button--danger" |
|
|
type="button" |
|
|
id="stop-simulation-btn" |
|
|
style="display: none;" |
|
|
> |
|
|
Stop |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="trackio-container"> |
|
|
<Trackio client:load variant="classic" logScaleX={true} smoothing={true} /> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
document.addEventListener("DOMContentLoaded", async () => { |
|
|
const themeSelect = document.getElementById("theme-select"); |
|
|
const randomizeBtn = document.getElementById("randomize-btn"); |
|
|
const startSimulationBtn = document.getElementById("start-simulation-btn"); |
|
|
const stopSimulationBtn = document.getElementById("stop-simulation-btn"); |
|
|
const logScaleXCheckbox = document.getElementById("log-scale-x"); |
|
|
const smoothDataCheckbox = document.getElementById("smooth-data"); |
|
|
const trackioContainer = document.querySelector(".trackio-container"); |
|
|
|
|
|
if ( |
|
|
!themeSelect || |
|
|
!randomizeBtn || |
|
|
!startSimulationBtn || |
|
|
!stopSimulationBtn || |
|
|
!logScaleXCheckbox || |
|
|
!smoothDataCheckbox || |
|
|
!trackioContainer |
|
|
) |
|
|
return; |
|
|
|
|
|
|
|
|
let simulationInterval = null; |
|
|
let currentSimulationRun = null; |
|
|
let currentStep = 0; |
|
|
|
|
|
|
|
|
const { triggerJitter } = await import("./core/store.js"); |
|
|
|
|
|
|
|
|
themeSelect.addEventListener("change", (e) => { |
|
|
const target = e.target; |
|
|
if (!target || !("value" in target)) return; |
|
|
|
|
|
const newVariant = target.value; |
|
|
console.log(`Theme changed to: ${newVariant}`); |
|
|
|
|
|
|
|
|
const trackioEl = debugTrackioState(); |
|
|
if (trackioEl && trackioEl.__trackioInstance) { |
|
|
console.log("✅ Calling setTheme on Trackio instance"); |
|
|
trackioEl.__trackioInstance.setTheme(newVariant); |
|
|
} else { |
|
|
console.warn("❌ No Trackio instance found for theme change"); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
logScaleXCheckbox.addEventListener("change", (e) => { |
|
|
const target = e.target; |
|
|
if (!target || !("checked" in target)) return; |
|
|
|
|
|
const isLogScale = target.checked; |
|
|
console.log(`Log scale X changed to: ${isLogScale}`); |
|
|
|
|
|
|
|
|
const trackioEl = debugTrackioState(); |
|
|
if (trackioEl && trackioEl.__trackioInstance) { |
|
|
console.log("✅ Calling setLogScaleX on Trackio instance"); |
|
|
trackioEl.__trackioInstance.setLogScaleX(isLogScale); |
|
|
} else { |
|
|
console.warn("❌ Trackio instance not found for log scale change"); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
smoothDataCheckbox.addEventListener("change", (e) => { |
|
|
const target = e.target; |
|
|
if (!target || !("checked" in target)) return; |
|
|
|
|
|
const isSmooth = target.checked; |
|
|
console.log(`Smooth data changed to: ${isSmooth}`); |
|
|
|
|
|
|
|
|
const trackioEl = debugTrackioState(); |
|
|
if (trackioEl && trackioEl.__trackioInstance) { |
|
|
console.log("✅ Calling setSmoothing on Trackio instance"); |
|
|
trackioEl.__trackioInstance.setSmoothing(isSmooth); |
|
|
} else { |
|
|
console.warn("❌ Trackio instance not found for smooth change"); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
function debugTrackioState() { |
|
|
const trackioEl = trackioContainer.querySelector(".trackio"); |
|
|
console.log("🔍 Debug Trackio state:", { |
|
|
container: !!trackioContainer, |
|
|
trackioEl: !!trackioEl, |
|
|
hasInstance: !!(trackioEl && trackioEl.__trackioInstance), |
|
|
availableMethods: |
|
|
trackioEl && trackioEl.__trackioInstance |
|
|
? Object.keys(trackioEl.__trackioInstance) |
|
|
: "none", |
|
|
windowInstance: !!window.trackioInstance, |
|
|
}); |
|
|
return trackioEl; |
|
|
} |
|
|
|
|
|
|
|
|
function initializeTrackio(attempt = 1) { |
|
|
console.log(`🚀 Initializing Trackio (attempt ${attempt})`); |
|
|
|
|
|
const trackioEl = debugTrackioState(); |
|
|
|
|
|
if (trackioEl && trackioEl.__trackioInstance) { |
|
|
console.log("✅ Trackio instance found, applying initial settings"); |
|
|
|
|
|
if (logScaleXCheckbox.checked) { |
|
|
console.log("Initializing with log scale X enabled"); |
|
|
trackioEl.__trackioInstance.setLogScaleX(true); |
|
|
} |
|
|
|
|
|
if (smoothDataCheckbox.checked) { |
|
|
console.log("Initializing with smoothing enabled"); |
|
|
trackioEl.__trackioInstance.setSmoothing(true); |
|
|
} |
|
|
} else { |
|
|
console.log("❌ Trackio instance not ready yet"); |
|
|
if (attempt < 10) { |
|
|
setTimeout(() => initializeTrackio(attempt + 1), 200 * attempt); |
|
|
} else { |
|
|
console.error("Failed to initialize Trackio after 10 attempts"); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
setTimeout(() => initializeTrackio(), 100); |
|
|
|
|
|
|
|
|
function generateSimulatedValue(step, metric) { |
|
|
const baseProgress = Math.min(1, step / 100); |
|
|
|
|
|
if (metric === "loss") { |
|
|
|
|
|
const baseLoss = 2.0 * Math.exp(-0.05 * step); |
|
|
const noise = (Math.random() - 0.5) * 0.2; |
|
|
return Math.max(0.01, baseLoss + noise); |
|
|
} else if (metric === "accuracy") { |
|
|
|
|
|
const baseAcc = 0.1 + 0.8 * (1 - Math.exp(-0.04 * step)); |
|
|
const noise = (Math.random() - 0.5) * 0.05; |
|
|
return Math.max(0, Math.min(1, baseAcc + noise)); |
|
|
} |
|
|
return Math.random(); |
|
|
} |
|
|
|
|
|
|
|
|
function startSimulation() { |
|
|
if (simulationInterval) { |
|
|
clearInterval(simulationInterval); |
|
|
} |
|
|
|
|
|
|
|
|
const adjectives = [ |
|
|
"live", |
|
|
"real-time", |
|
|
"streaming", |
|
|
"dynamic", |
|
|
"active", |
|
|
"running", |
|
|
]; |
|
|
const nouns = [ |
|
|
"experiment", |
|
|
"trial", |
|
|
"session", |
|
|
"training", |
|
|
"run", |
|
|
"test", |
|
|
]; |
|
|
const randomAdj = |
|
|
adjectives[Math.floor(Math.random() * adjectives.length)]; |
|
|
const randomNoun = nouns[Math.floor(Math.random() * nouns.length)]; |
|
|
currentSimulationRun = `${randomAdj}-${randomNoun}-${Date.now().toString().slice(-4)}`; |
|
|
currentStep = 1; |
|
|
|
|
|
console.log(`Starting simulation for run: ${currentSimulationRun}`); |
|
|
|
|
|
|
|
|
startSimulationBtn.style.display = "none"; |
|
|
stopSimulationBtn.style.display = "inline-flex"; |
|
|
startSimulationBtn.disabled = true; |
|
|
|
|
|
|
|
|
addSimulationStep(); |
|
|
|
|
|
|
|
|
simulationInterval = setInterval(() => { |
|
|
currentStep++; |
|
|
addSimulationStep(); |
|
|
|
|
|
|
|
|
if (currentStep > 200) { |
|
|
stopSimulation(); |
|
|
} |
|
|
}, 1000); |
|
|
} |
|
|
|
|
|
|
|
|
function addSimulationStep() { |
|
|
const trackioEl = trackioContainer.querySelector(".trackio"); |
|
|
if (trackioEl && trackioEl.__trackioInstance) { |
|
|
const newDataPoint = { |
|
|
step: currentStep, |
|
|
loss: generateSimulatedValue(currentStep, "loss"), |
|
|
accuracy: generateSimulatedValue(currentStep, "accuracy"), |
|
|
}; |
|
|
|
|
|
console.log( |
|
|
`Adding simulation step ${currentStep} for run ${currentSimulationRun}:`, |
|
|
newDataPoint, |
|
|
); |
|
|
|
|
|
|
|
|
if ( |
|
|
typeof trackioEl.__trackioInstance.addLiveDataPoint === "function" |
|
|
) { |
|
|
trackioEl.__trackioInstance.addLiveDataPoint( |
|
|
currentSimulationRun, |
|
|
newDataPoint, |
|
|
); |
|
|
} else { |
|
|
console.warn("addLiveDataPoint method not found on Trackio instance"); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function stopSimulation() { |
|
|
if (simulationInterval) { |
|
|
clearInterval(simulationInterval); |
|
|
simulationInterval = null; |
|
|
} |
|
|
|
|
|
console.log(`Stopping simulation for run: ${currentSimulationRun}`); |
|
|
|
|
|
|
|
|
startSimulationBtn.style.display = "inline-flex"; |
|
|
stopSimulationBtn.style.display = "none"; |
|
|
startSimulationBtn.disabled = false; |
|
|
|
|
|
currentSimulationRun = null; |
|
|
currentStep = 0; |
|
|
} |
|
|
|
|
|
|
|
|
startSimulationBtn.addEventListener("click", startSimulation); |
|
|
stopSimulationBtn.addEventListener("click", stopSimulation); |
|
|
|
|
|
|
|
|
window.addEventListener("beforeunload", stopSimulation); |
|
|
|
|
|
|
|
|
randomizeBtn.addEventListener("click", () => { |
|
|
console.log("Randomize button clicked - triggering jitter via store"); |
|
|
|
|
|
|
|
|
if (simulationInterval) { |
|
|
stopSimulation(); |
|
|
} |
|
|
|
|
|
|
|
|
randomizeBtn.classList.add("vibrating"); |
|
|
setTimeout(() => { |
|
|
randomizeBtn.classList.remove("vibrating"); |
|
|
}, 600); |
|
|
|
|
|
|
|
|
if ( |
|
|
window.trackioInstance && |
|
|
typeof window.trackioInstance.jitterData === "function" |
|
|
) { |
|
|
console.log( |
|
|
"Found window.trackioInstance, calling jitterData directly", |
|
|
); |
|
|
window.trackioInstance.jitterData(); |
|
|
} else { |
|
|
console.log("No window.trackioInstance found, using store trigger"); |
|
|
triggerJitter(); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
</script> |
|
|
|
|
|
<style> |
|
|
.trackio-wrapper { |
|
|
width: 100%; |
|
|
margin: 0px 0 20px 0; |
|
|
} |
|
|
|
|
|
.trackio-controls { |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
margin-bottom: 16px; |
|
|
padding: 12px 0px; |
|
|
|
|
|
gap: 16px; |
|
|
flex-wrap: nowrap; |
|
|
} |
|
|
|
|
|
.controls-left { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 24px; |
|
|
flex-wrap: wrap; |
|
|
} |
|
|
|
|
|
.controls-right { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 12px; |
|
|
flex-wrap: wrap; |
|
|
} |
|
|
|
|
|
.btn-randomize { |
|
|
display: inline-flex; |
|
|
align-items: center; |
|
|
gap: 6px; |
|
|
padding: 8px 16px; |
|
|
background: var(--accent-color, #007acc); |
|
|
color: white; |
|
|
border: none; |
|
|
border-radius: 6px; |
|
|
font-size: 14px; |
|
|
font-weight: 500; |
|
|
cursor: pointer; |
|
|
transition: all 0.15s ease; |
|
|
} |
|
|
|
|
|
.btn-randomize:hover { |
|
|
background: var(--accent-hover, #005a9e); |
|
|
transform: translateY(-1px); |
|
|
} |
|
|
|
|
|
.btn-randomize:active { |
|
|
transform: translateY(0); |
|
|
} |
|
|
|
|
|
.theme-selector { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 8px; |
|
|
font-size: 14px; |
|
|
flex-shrink: 0; |
|
|
white-space: nowrap; |
|
|
} |
|
|
|
|
|
.theme-selector label { |
|
|
font-weight: 500; |
|
|
color: var(--text-color); |
|
|
} |
|
|
|
|
|
.theme-select { |
|
|
padding: 6px 12px; |
|
|
border: 1px solid var(--border-color); |
|
|
border-radius: 4px; |
|
|
background: var(--input-bg, var(--surface-bg)); |
|
|
color: var(--text-color); |
|
|
font-size: 14px; |
|
|
cursor: pointer; |
|
|
transition: border-color 0.15s ease; |
|
|
} |
|
|
|
|
|
.theme-select:focus { |
|
|
outline: none; |
|
|
border-color: var(--accent-color, #007acc); |
|
|
} |
|
|
|
|
|
.scale-controls { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 16px; |
|
|
flex-shrink: 0; |
|
|
white-space: nowrap; |
|
|
} |
|
|
|
|
|
|
|
|
@keyframes vibrate { |
|
|
0% { |
|
|
transform: translateX(0); |
|
|
} |
|
|
10% { |
|
|
transform: translateX(-2px) rotate(-1deg); |
|
|
} |
|
|
20% { |
|
|
transform: translateX(2px) rotate(1deg); |
|
|
} |
|
|
30% { |
|
|
transform: translateX(-2px) rotate(-1deg); |
|
|
} |
|
|
40% { |
|
|
transform: translateX(2px) rotate(1deg); |
|
|
} |
|
|
50% { |
|
|
transform: translateX(-1px) rotate(-0.5deg); |
|
|
} |
|
|
60% { |
|
|
transform: translateX(1px) rotate(0.5deg); |
|
|
} |
|
|
70% { |
|
|
transform: translateX(-1px) rotate(-0.5deg); |
|
|
} |
|
|
80% { |
|
|
transform: translateX(1px) rotate(0.5deg); |
|
|
} |
|
|
90% { |
|
|
transform: translateX(-0.5px) rotate(-0.25deg); |
|
|
} |
|
|
100% { |
|
|
transform: translateX(0) rotate(0); |
|
|
} |
|
|
} |
|
|
|
|
|
.button.vibrating { |
|
|
animation: vibrate 0.6s ease-in-out; |
|
|
} |
|
|
|
|
|
.trackio-container { |
|
|
width: 100%; |
|
|
margin-top: 10px; |
|
|
border: 1px solid var(--border-color); |
|
|
padding: 24px 12px; |
|
|
} |
|
|
|
|
|
@media (max-width: 768px) { |
|
|
.trackio-controls { |
|
|
flex-direction: column; |
|
|
align-items: stretch; |
|
|
gap: 12px; |
|
|
} |
|
|
|
|
|
.controls-left { |
|
|
flex-direction: column; |
|
|
align-items: stretch; |
|
|
gap: 12px; |
|
|
} |
|
|
|
|
|
.theme-selector { |
|
|
justify-content: space-between; |
|
|
} |
|
|
|
|
|
.scale-controls { |
|
|
justify-content: space-between; |
|
|
} |
|
|
} |
|
|
</style> |
|
|
|