From 56d1f7e11e98b8b4697511d71c1d6b802728ea0e Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Sat, 17 Jan 2026 22:58:24 -0700 Subject: [PATCH] Improved analysis screen on focused mode. --- bicorder-app/src/App.svelte | 116 +++++- .../AnalysisTransitionBanner.svelte | 359 ++++++++++++++++++ .../src/components/FormRecommendation.svelte | 59 +-- 3 files changed, 469 insertions(+), 65 deletions(-) create mode 100644 bicorder-app/src/components/AnalysisTransitionBanner.svelte diff --git a/bicorder-app/src/App.svelte b/bicorder-app/src/App.svelte index ab94867..c655772 100644 --- a/bicorder-app/src/App.svelte +++ b/bicorder-app/src/App.svelte @@ -7,6 +7,7 @@ import ExportControls from './components/ExportControls.svelte'; import HelpModal from './components/HelpModal.svelte'; import FormRecommendation from './components/FormRecommendation.svelte'; + import AnalysisTransitionBanner from './components/AnalysisTransitionBanner.svelte'; import { BicorderClassifier } from './bicorder-classifier'; // Load bicorder data and model from build-time constants @@ -57,12 +58,12 @@ }); }); - // Analysis screens (not in shortform) - if (!isShortForm) { - data.analysis.forEach((gradient, index) => { - screens.push({ type: 'analysis', index, gradient }); - }); - } + // Analysis screens (shown in both shortform and longform) + // Show useful/not-useful gradient first (index 3), then the others + const analysisOrder = [3, 0, 1, 2]; // useful/not-useful, hardness, polarization, bureaucratic + analysisOrder.forEach((index) => { + screens.push({ type: 'analysis', index, gradient: data.analysis[index] }); + }); // Export screen screens.push({ type: 'export' }); @@ -106,7 +107,80 @@ return filled.repeat(filledCount) + empty.repeat(emptyCount); } - $: progressBar = generateProgressBar(currentScreen + 1, totalScreens); + // Calculate total diagnostic screens (metadata + gradients only) + $: diagnosticScreenCount = screens.filter(s => s.type === 'metadata' || s.type === 'gradient').length; + + // Calculate current position within diagnostic screens + $: diagnosticProgress = currentScreenData?.type === 'metadata' || currentScreenData?.type === 'gradient' + ? currentScreen + 1 + : diagnosticScreenCount; // Show as complete when in analysis or export + + $: progressBar = generateProgressBar(diagnosticProgress, diagnosticScreenCount); + + // Detect if we're on the first analysis screen + $: isFirstAnalysisScreen = currentScreenData?.type === 'analysis' && + screens.findIndex(s => s.type === 'analysis') === currentScreen; + + // Calculate completed gradients for the banner + $: completedGradientsCount = data.diagnostic + .flatMap(set => set.gradients) + .filter(g => !data.metadata.shortform || g.shortform) + .filter(g => g.value !== null).length; + + $: totalGradientsCount = data.diagnostic + .flatMap(set => set.gradients) + .filter(g => !data.metadata.shortform || g.shortform).length; + + // Calculate form recommendation (shared by FormRecommendation and AnalysisTransitionBanner) + let formRecommendation: any = null; + let hasEnoughDataForRecommendation = false; + + $: { + // Collect ratings from diagnostic data + const ratings: Record = {}; + let valueCount = 0; + let shortFormTotal = 0; + + data.diagnostic.forEach((diagnosticSet) => { + const setName = diagnosticSet.set_name; + diagnosticSet.gradients.forEach((gradient) => { + // Count shortform gradients + if (gradient.shortform) { + shortFormTotal++; + } + + if (gradient.value !== null) { + const dimensionName = `${setName}_${gradient.term_left}_vs_${gradient.term_right}`; + ratings[dimensionName] = gradient.value; + + // Only count shortform values for the threshold + if (gradient.shortform) { + valueCount++; + } + } + }); + }); + + // Only calculate if at least half of shortform gradients are complete + const threshold = Math.ceil(shortFormTotal / 2); + hasEnoughDataForRecommendation = valueCount >= threshold; + + if (hasEnoughDataForRecommendation && data.metadata.shortform) { + try { + const prediction = classifier.predict(ratings, { detailed: true }); + const assessment = classifier.assessShortFormReadiness(ratings); + formRecommendation = { + ...prediction, + ...assessment, + }; + } catch (error) { + console.error('Error calculating form recommendation:', error); + formRecommendation = null; + } + } else { + formRecommendation = null; + } + } // Load saved state from localStorage onMount(() => { @@ -351,8 +425,8 @@
@@ -480,6 +554,28 @@
ANALYSIS
+ {#if isFirstAnalysisScreen} + { + // Jump to the export screen (last screen) + currentScreen = screens.length - 1; + }} + on:updateAnalysis={(e) => { + data.analysis[e.detail.index].value = e.detail.value; + data = data; + }} + on:updateAnalysisNotes={(e) => { + data.analysis[e.detail.index].notes = e.detail.notes; + data = data; + }} + /> + {/if} +
← {screen.gradient.term_left}
@@ -554,7 +650,7 @@ > {progressBar}
-
{currentScreen + 1} / {totalScreens}
+
{diagnosticProgress} / {diagnosticScreenCount}
{/if} diff --git a/bicorder-app/src/components/AnalysisTransitionBanner.svelte b/bicorder-app/src/components/AnalysisTransitionBanner.svelte new file mode 100644 index 0000000..42e47c3 --- /dev/null +++ b/bicorder-app/src/components/AnalysisTransitionBanner.svelte @@ -0,0 +1,359 @@ + + +
+ +
+ + diff --git a/bicorder-app/src/components/FormRecommendation.svelte b/bicorder-app/src/components/FormRecommendation.svelte index af1ffa3..a729d0b 100644 --- a/bicorder-app/src/components/FormRecommendation.svelte +++ b/bicorder-app/src/components/FormRecommendation.svelte @@ -1,9 +1,8 @@