diff --git a/web/src/App.vue b/web/src/App.vue index be6b3cb..b229fa1 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -6,16 +6,20 @@ import FooterDisclaimer from '@/components/FooterDisclaimer.vue' import HeroHeadline from '@/components/HeroHeadline.vue' import LegendRow from '@/components/LegendRow.vue' import PanelGrid from '@/components/PanelGrid.vue' +import PlayerBar from '@/components/PlayerBar.vue' import ResultsTable from '@/components/ResultsTable.vue' import ScenarioToolbar from '@/components/ScenarioToolbar.vue' +import { usePlayback } from '@/composables/usePlayback' import { useSimStore } from '@/composables/useSimStore' import { useTheme } from '@/composables/useTheme' const store = useSimStore() +const playback = usePlayback() useTheme() // resolve and apply the theme before first paint of the app -onMounted(() => { - void store.initialize() +onMounted(async () => { + await store.initialize() + playback.autoplayOnce() }) @@ -27,6 +31,7 @@ onMounted(() => { + { /> +
{{ store.state.announcement }}
diff --git a/web/src/components/PlayerBar.vue b/web/src/components/PlayerBar.vue new file mode 100644 index 0000000..b895e23 --- /dev/null +++ b/web/src/components/PlayerBar.vue @@ -0,0 +1,331 @@ + + + + + diff --git a/web/src/components/ScenarioPanel.vue b/web/src/components/ScenarioPanel.vue index f91fbb9..9f25190 100644 --- a/web/src/components/ScenarioPanel.vue +++ b/web/src/components/ScenarioPanel.vue @@ -2,7 +2,7 @@ import { computed } from 'vue' import NetworkView from './NetworkView.vue' import { useSimStore } from '@/composables/useSimStore' -import { formatPct, roundPct } from '@/lib/format' +import { roundPct } from '@/lib/format' import type { PanelSpec } from '@/presets/types' const props = defineProps<{ panel: PanelSpec; accent: string }>() @@ -11,10 +11,19 @@ const store = useSimStore() const result = computed(() => store.state.resultsByPanelId[props.panel.id]) const numStudents = computed(() => store.effectiveConfig(props.panel).numStudents) + +// The numbers follow the playhead: they count what the network picture +// shows at the current round, reaching the final outcome on the last one. +const reachedCount = computed(() => { + const panelResult = result.value + if (!panelResult) return 0 + return panelResult.reachedAtRound.filter( + (reachedAt) => reachedAt >= 0 && reachedAt <= store.state.round, + ).length +}) +const reachedPctNow = computed(() => (reachedCount.value / numStudents.value) * 100) const tone = computed(() => - result.value && roundPct(result.value.reachedPct) <= store.preset.toneThresholdPct - ? 'good' - : 'bad', + roundPct(reachedPctNow.value) <= store.preset.toneThresholdPct ? 'good' : 'bad', ) interface PanelChip { @@ -48,12 +57,9 @@ const dimmed = computed(() => store.state.runState === 'running' && result.value